Ho intenzione di usarlo con JavaScript per ritagliare un'immagine per adattarla all'intera finestra.
Modifica : userò un componente di terze parti che accetta solo le proporzioni nel formato come: 4:3
, 16:9
.
Ho intenzione di usarlo con JavaScript per ritagliare un'immagine per adattarla all'intera finestra.
Modifica : userò un componente di terze parti che accetta solo le proporzioni nel formato come: 4:3
, 16:9
.
Risposte:
Immagino che tu stia cercando una integer:integer
soluzione di proporzioni utilizzabile come 16:9
piuttosto che una float:1
soluzione come 1.77778:1
.
In tal caso, quello che devi fare è trovare il massimo comune divisore (MCD) e dividere entrambi i valori per quello. Il GCD è il numero più alto che divide equamente entrambi i numeri. Quindi il MCD per 6 e 10 è 2, il MCD per 44 e 99 è 11.
Ad esempio, un monitor 1024x768 ha un MCD di 256. Quando dividi entrambi i valori per questo, ottieni 4x3 o 4: 3.
Un algoritmo GCD (ricorsivo):
function gcd (a,b):
if b == 0:
return a
return gcd (b, a mod b)
In C:
static int gcd (int a, int b) {
return (b == 0) ? a : gcd (b, a%b);
}
int main(void) {
printf ("gcd(1024,768) = %d\n",gcd(1024,768));
}
Ed ecco alcuni HTML / Javascript completi che mostrano un modo per rilevare le dimensioni dello schermo e calcolare le proporzioni da quello. Funziona in FF3, non sono sicuro di quale supporto abbiano altri browser per screen.width
e screen.height
.
<html><body>
<script type="text/javascript">
function gcd (a, b) {
return (b == 0) ? a : gcd (b, a%b);
}
var w = screen.width;
var h = screen.height;
var r = gcd (w, h);
document.write ("<pre>");
document.write ("Dimensions = ", w, " x ", h, "<br>");
document.write ("Gcd = ", r, "<br>");
document.write ("Aspect = ", w/r, ":", h/r);
document.write ("</pre>");
</script>
</body></html>
Emette (sul mio strano monitor widescreen):
Dimensions = 1680 x 1050
Gcd = 210
Aspect = 8:5
Altri su cui l'ho testato:
Dimensions = 1280 x 1024
Gcd = 256
Aspect = 5:4
Dimensions = 1152 x 960
Gcd = 192
Aspect = 6:5
Dimensions = 1280 x 960
Gcd = 320
Aspect = 4:3
Dimensions = 1920 x 1080
Gcd = 120
Aspect = 16:9
Vorrei averlo avuto l'ultima a casa ma, no, purtroppo è una macchina da lavoro.
Quello che fai se scopri che le proporzioni non sono supportate dal tuo strumento di ridimensionamento grafico è un'altra questione. Sospetto che la scommessa migliore sarebbe quella di aggiungere linee di boxe delle lettere (come quelle che ottieni in cima e in fondo alla tua vecchia TV quando guardi un film a grande schermo). Li aggiungerei in alto / in basso o ai lati (a seconda di quale risulta il minor numero di linee di letter boxing) fino a quando l'immagine non soddisfa i requisiti.
Una cosa che potresti voler considerare è la qualità di un'immagine che è stata cambiata da 16: 9 a 5: 4 - Ricordo ancora i cowboy incredibilmente alti e magri che guardavo in gioventù in televisione prima dell'introduzione del letterboxing. Potrebbe essere meglio avere un'immagine diversa per proporzioni e ridimensionare quella corretta per le dimensioni effettive dello schermo prima di inviarla lungo il cavo.
728x90
-> 364:45
non sono sicuro che sia il risultato desiderato
La risposta di paxdiablo è ottima, ma ci sono molte risoluzioni comuni che hanno solo pochi pixel in più o in meno in una determinata direzione, e l'approccio con il massimo comune divisore dà loro risultati orribili.
Prendiamo ad esempio la risoluzione ben educata di 1360x765 che offre un bel rapporto 16: 9 utilizzando l'approccio gcd. Secondo Steam, questa risoluzione viene utilizzata solo dallo 0,01% dei suoi utenti, mentre 1366x768 viene utilizzata da un 18,9%. Vediamo cosa otteniamo utilizzando l'approccio gcd:
1360x765 - 16:9 (0.01%)
1360x768 - 85:48 (2.41%)
1366x768 - 683:384 (18.9%)
Vorremmo arrotondare il rapporto 683: 384 al rapporto più vicino, 16: 9.
Ho scritto uno script Python che analizza un file di testo con numeri incollati dalla pagina del sondaggio Steam Hardware e stampa tutte le risoluzioni e i rapporti noti più vicini, nonché la prevalenza di ciascun rapporto (che era il mio obiettivo quando ho iniziato questo):
# Contents pasted from store.steampowered.com/hwsurvey, section 'Primary Display Resolution'
steam_file = './steam.txt'
# Taken from http://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/Vector_Video_Standards4.svg/750px-Vector_Video_Standards4.svg.png
accepted_ratios = ['5:4', '4:3', '3:2', '8:5', '5:3', '16:9', '17:9']
#-------------------------------------------------------
def gcd(a, b):
if b == 0: return a
return gcd (b, a % b)
#-------------------------------------------------------
class ResData:
#-------------------------------------------------------
# Expected format: 1024 x 768 4.37% -0.21% (w x h prevalence% change%)
def __init__(self, steam_line):
tokens = steam_line.split(' ')
self.width = int(tokens[0])
self.height = int(tokens[2])
self.prevalence = float(tokens[3].replace('%', ''))
# This part based on pixdiablo's gcd answer - http://stackoverflow.com/a/1186465/828681
common = gcd(self.width, self.height)
self.ratio = str(self.width / common) + ':' + str(self.height / common)
self.ratio_error = 0
# Special case: ratio is not well behaved
if not self.ratio in accepted_ratios:
lesser_error = 999
lesser_index = -1
my_ratio_normalized = float(self.width) / float(self.height)
# Check how far from each known aspect this resolution is, and take one with the smaller error
for i in range(len(accepted_ratios)):
ratio = accepted_ratios[i].split(':')
w = float(ratio[0])
h = float(ratio[1])
known_ratio_normalized = w / h
distance = abs(my_ratio_normalized - known_ratio_normalized)
if (distance < lesser_error):
lesser_index = i
lesser_error = distance
self.ratio_error = distance
self.ratio = accepted_ratios[lesser_index]
#-------------------------------------------------------
def __str__(self):
descr = str(self.width) + 'x' + str(self.height) + ' - ' + self.ratio + ' - ' + str(self.prevalence) + '%'
if self.ratio_error > 0:
descr += ' error: %.2f' % (self.ratio_error * 100) + '%'
return descr
#-------------------------------------------------------
# Returns a list of ResData
def parse_steam_file(steam_file):
result = []
for line in file(steam_file):
result.append(ResData(line))
return result
#-------------------------------------------------------
ratios_prevalence = {}
data = parse_steam_file(steam_file)
print('Known Steam resolutions:')
for res in data:
print(res)
acc_prevalence = ratios_prevalence[res.ratio] if (res.ratio in ratios_prevalence) else 0
ratios_prevalence[res.ratio] = acc_prevalence + res.prevalence
# Hack to fix 8:5, more known as 16:10
ratios_prevalence['16:10'] = ratios_prevalence['8:5']
del ratios_prevalence['8:5']
print('\nSteam screen ratio prevalences:')
sorted_ratios = sorted(ratios_prevalence.items(), key=lambda x: x[1], reverse=True)
for value in sorted_ratios:
print(value[0] + ' -> ' + str(value[1]) + '%')
Per i curiosi, queste sono la prevalenza delle proporzioni dello schermo tra gli utenti di Steam (a partire da ottobre 2012):
16:9 -> 58.9%
16:10 -> 24.0%
5:4 -> 9.57%
4:3 -> 6.38%
5:3 -> 0.84%
17:9 -> 0.11%
Immagino che tu voglia decidere quale tra 4: 3 e 16: 9 è la soluzione migliore.
function getAspectRatio(width, height) {
var ratio = width / height;
return ( Math.abs( ratio - 4 / 3 ) < Math.abs( ratio - 16 / 9 ) ) ? '4:3' : '16:9';
}
Ecco una versione del miglior algoritmo di approssimazione razionale di James Farey con livello di sfocatura regolabile portato su javascript dal codice di calcolo delle proporzioni scritto originariamente in python.
Il metodo accetta un float ( width/height
) e un limite superiore per il numeratore / denominatore della frazione.
Nell'esempio seguente sto impostando un limite superiore 50
perché ho bisogno di 1035x582
(1.77835051546) da trattare come 16:9
(1.77777777778) piuttosto che 345:194
come si ottiene con l' gcd
algoritmo semplice elencato in altre risposte.
<html>
<body>
<script type="text/javascript">
function aspect_ratio(val, lim) {
var lower = [0, 1];
var upper = [1, 0];
while (true) {
var mediant = [lower[0] + upper[0], lower[1] + upper[1]];
if (val * mediant[1] > mediant[0]) {
if (lim < mediant[1]) {
return upper;
}
lower = mediant;
} else if (val * mediant[1] == mediant[0]) {
if (lim >= mediant[1]) {
return mediant;
}
if (lower[1] < upper[1]) {
return lower;
}
return upper;
} else {
if (lim < mediant[1]) {
return lower;
}
upper = mediant;
}
}
}
document.write (aspect_ratio(800 / 600, 50) +"<br/>");
document.write (aspect_ratio(1035 / 582, 50) + "<br/>");
document.write (aspect_ratio(2560 / 1440, 50) + "<br/>");
</script>
</body></html>
Il risultato:
4,3 // (1.33333333333) (800 x 600)
16,9 // (1.77777777778) (2560.0 x 1440)
16,9 // (1.77835051546) (1035.0 x 582)
Nel caso tu sia un maniaco delle prestazioni ...
Il modo più veloce (in JavaScript) per calcolare un rapporto rettangolo è utilizzare un vero algoritmo binario Great Common Divisor.
(Tutti i test di velocità e timing sono stati eseguiti da altri, puoi controllare un benchmark qui: https://lemire.me/blog/2013/12/26/fastest-way-to-compute-the-greatest-common-divisor / )
Eccolo:
/* the binary Great Common Divisor calculator */
function gcd (u, v) {
if (u === v) return u;
if (u === 0) return v;
if (v === 0) return u;
if (~u & 1)
if (v & 1)
return gcd(u >> 1, v);
else
return gcd(u >> 1, v >> 1) << 1;
if (~v & 1) return gcd(u, v >> 1);
if (u > v) return gcd((u - v) >> 1, v);
return gcd((v - u) >> 1, u);
}
/* returns an array with the ratio */
function ratio (w, h) {
var d = gcd(w,h);
return [w/d, h/d];
}
/* example */
var r1 = ratio(1600, 900);
var r2 = ratio(1440, 900);
var r3 = ratio(1366, 768);
var r4 = ratio(1280, 1024);
var r5 = ratio(1280, 720);
var r6 = ratio(1024, 768);
/* will output this:
r1: [16, 9]
r2: [8, 5]
r3: [683, 384]
r4: [5, 4]
r5: [16, 9]
r6: [4, 3]
*/
Ecco la mia soluzione che è piuttosto semplice poiché tutto ciò che mi interessa non è necessariamente MCD o anche rapporti accurati: perché allora ottieni cose strane come 345/113 che non sono comprensibili dall'uomo.
Fondamentalmente ho impostato rapporti orizzontali o verticali accettabili e il loro "valore" come float ... quindi confronto la mia versione float del rapporto con ciascuno e che abbia mai la differenza di valore assoluto più basso è il rapporto più vicino all'elemento. In questo modo quando l'utente fa 16: 9 ma poi rimuove 10 pixel dal fondo, conta ancora come 16: 9 ...
accepted_ratios = {
'landscape': (
(u'5:4', 1.25),
(u'4:3', 1.33333333333),
(u'3:2', 1.5),
(u'16:10', 1.6),
(u'5:3', 1.66666666667),
(u'16:9', 1.77777777778),
(u'17:9', 1.88888888889),
(u'21:9', 2.33333333333),
(u'1:1', 1.0)
),
'portrait': (
(u'4:5', 0.8),
(u'3:4', 0.75),
(u'2:3', 0.66666666667),
(u'10:16', 0.625),
(u'3:5', 0.6),
(u'9:16', 0.5625),
(u'9:17', 0.5294117647),
(u'9:21', 0.4285714286),
(u'1:1', 1.0)
),
}
def find_closest_ratio(ratio):
lowest_diff, best_std = 9999999999, '1:1'
layout = 'portrait' if ratio < 1.0 else 'landscape'
for pretty_str, std_ratio in accepted_ratios[layout]:
diff = abs(std_ratio - ratio)
if diff < lowest_diff:
lowest_diff = diff
best_std = pretty_str
return best_std
def extract_ratio(width, height):
try:
divided = float(width)/float(height)
if divided == 1.0: return '1:1'
return find_closest_ratio(divided)
except TypeError:
return None
Immagino che tu stia parlando di video qui, nel qual caso potresti anche dover preoccuparti delle proporzioni dei pixel del video sorgente. Per esempio.
PAL DV ha una risoluzione di 720x576. Che sarebbe come il suo 4: 3. Ora, a seconda delle proporzioni dei pixel (PAR), le proporzioni dello schermo possono essere 4: 3 o 16: 9.
Per maggiori informazioni dai un'occhiata qui http://en.wikipedia.org/wiki/Pixel_aspect_ratio
Puoi ottenere il rapporto di aspetto pixel quadrato e molti video web lo sono, ma potresti voler guardare fuori dagli altri casi.
Spero che sia di aiuto
marchio
Sulla base delle altre risposte, ecco come ho ottenuto i numeri di cui avevo bisogno in Python;
from decimal import Decimal
def gcd(a,b):
if b == 0:
return a
return gcd(b, a%b)
def closest_aspect_ratio(width, height):
g = gcd(width, height)
x = Decimal(str(float(width)/float(g)))
y = Decimal(str(float(height)/float(g)))
dec = Decimal(str(x/y))
return dict(x=x, y=y, dec=dec)
>>> closest_aspect_ratio(1024, 768)
{'y': Decimal('3.0'),
'x': Decimal('4.0'),
'dec': Decimal('1.333333333333333333333333333')}
Credo che le proporzioni siano la larghezza divisa per l'altezza.
r = w/h
Penso che questo faccia quello che chiedi:
webdeveloper.com - da decimale a frazione
Larghezza / altezza ti dà un decimale, convertito in una frazione con ":" al posto di "/" ti dà un "rapporto".
Questo algoritmo in Python ti fa fare parte del percorso.
Dimmi cosa succede se le finestre hanno una dimensione buffa.
Forse quello che dovresti avere è un elenco di tutti i rapporti accettabili (rispetto al componente di terze parti). Quindi, trova la corrispondenza più vicina alla tua finestra e restituisci quel rapporto dall'elenco.
nel mio caso voglio qualcosa di simile
[10,5,15,20,25] -> [2, 1, 3, 4, 5]
function ratio(array){
let min = Math.min(...array);
let ratio = array.map((element)=>{
return element/min;
});
return ratio;
}
document.write(ratio([10,5,15,20,25])); // [ 2, 1, 3, 4, 5 ]
Puoi sempre iniziare creando una tabella di ricerca basata su proporzioni comuni. Controlla https://en.wikipedia.org/wiki/Display_aspect_ratio Quindi puoi semplicemente fare la divisione
Per i problemi della vita reale, puoi fare qualcosa come di seguito
let ERROR_ALLOWED = 0.05
let STANDARD_ASPECT_RATIOS = [
[1, '1:1'],
[4/3, '4:3'],
[5/4, '5:4'],
[3/2, '3:2'],
[16/10, '16:10'],
[16/9, '16:9'],
[21/9, '21:9'],
[32/9, '32:9'],
]
let RATIOS = STANDARD_ASPECT_RATIOS.map(function(tpl){return tpl[0]}).sort()
let LOOKUP = Object()
for (let i=0; i < STANDARD_ASPECT_RATIOS.length; i++){
LOOKUP[STANDARD_ASPECT_RATIOS[i][0]] = STANDARD_ASPECT_RATIOS[i][1]
}
/*
Find the closest value in a sorted array
*/
function findClosest(arrSorted, value){
closest = arrSorted[0]
closestDiff = Math.abs(arrSorted[0] - value)
for (let i=1; i<arrSorted.length; i++){
let diff = Math.abs(arrSorted[i] - value)
if (diff < closestDiff){
closestDiff = diff
closest = arrSorted[i]
} else {
return closest
}
}
return arrSorted[arrSorted.length-1]
}
/*
Estimate the aspect ratio based on width x height (order doesn't matter)
*/
function estimateAspectRatio(dim1, dim2){
let ratio = Math.max(dim1, dim2) / Math.min(dim1, dim2)
if (ratio in LOOKUP){
return LOOKUP[ratio]
}
// Look by approximation
closest = findClosest(RATIOS, ratio)
if (Math.abs(closest - ratio) <= ERROR_ALLOWED){
return '~' + LOOKUP[closest]
}
return 'non standard ratio: ' + Math.round(ratio * 100) / 100 + ':1'
}
Quindi dai semplicemente le dimensioni in qualsiasi ordine
estimateAspectRatio(1920, 1080) // 16:9
estimateAspectRatio(1920, 1085) // ~16:9
estimateAspectRatio(1920, 1150) // non standard ratio: 1.65:1
estimateAspectRatio(1920, 1200) // 16:10
estimateAspectRatio(1920, 1220) // ~16:10