Sintassi descrittive

In questo modulo imparerai a consentire al browser di scegliere tra diverse immagini, in modo che possa prendere le migliori decisioni su cosa visualizzare. srcset non è un metodo per scambiare le origini delle immagini in punti di interruzione specifici e non è prevista la sostituzione di un'immagine con un'altra. Queste sintassi consentono un browser per risolvere un problema molto difficile, indipendentemente da noi: richiedere e visualizzare senza problemi un'origine di immagine adattata al contesto di navigazione dell'utente tra cui la dimensione dell'area visibile, la densità dello schermo, le preferenze dell'utente, la larghezza di banda e innumerevoli altri fattori.

È una richiesta importante. Sicuramente più di quanto ci teniamo a prendere in considerazione quando eseguiamo semplicemente il markup di un'immagine per il Web, e questo comporta anche molto informazioni a quelle a cui possiamo accedere.

Descrivere la densità con x

Un elemento <img> con larghezza fissa occuperà la stessa quantità di area visibile in qualsiasi contesto di navigazione, indipendentemente dalla compattezza display: il numero di pixel fisici che compongono lo schermo. Ad esempio, un'immagine con larghezza intrinseca di 400px occuperà quasi l'intera area visibile del browser sia sul Google Pixel originale sia sul Pixel 6 Pro più recente: entrambi i dispositivi hanno un 412px area visibile ampia in pixel logici.

Tuttavia, Pixel 6 Pro ha un display molto più nitido: il 6 Pro ha una risoluzione fisica di 1440 × 3120 pixel, mentre Il valore di Pixel è 1080 × 1920 pixel, ovvero il numero di pixel hardware che compongono lo schermo stesso.

Il rapporto tra i pixel logici e i pixel fisici di un dispositivo è il rapporto pixel del dispositivo per il display in questione (DPR). Il DPR è calcolata dividendo la risoluzione effettiva dello schermo del dispositivo per i pixel CSS dell'area visibile.

Un DPR pari a 2 visualizzato in una finestra della console.

Quindi, il Pixel originale ha un DPR di 2,6, mentre Pixel 6 Pro ha un DPR di 3,5.

iPhone 4, il primo dispositivo con un DPR maggiore di 1, riporta un rapporto pixel del dispositivo di 2: la risoluzione fisica dello schermo è il doppio della risoluzione logica. Qualsiasi dispositivo precedente all'iPhone 4 aveva una DPR di 1: un pixel logico a un pixel fisico.

Se visualizzi l'immagine a livello di 400px su un display con una DPR pari a 2, ogni pixel logico viene visualizzato in quattro i pixel fisici del display: due orizzontali e due verticali. L'immagine non sfrutta il display ad alta densità, ma avrà come su un display con un DPR di 1. Naturalmente, qualsiasi cosa "disegnata" dal motore di rendering del browser (testo, forme CSS o SVG), ad esempio, verranno disegnate in modo da adattarle al display a maggiore densità. Tuttavia, come hai appreso da Formati delle immagini e compressione, le immagini raster sono fisse griglie di pixel. Sebbene non sia sempre ovvio, un'immagine raster migliorata per adattarsi a uno schermo a densità più elevata apparirà a una risoluzione bassa rispetto alla pagina circostante.

Per evitare questo upscaling, l'immagine visualizzata deve avere una larghezza intrinseca di almeno 800 pixel. Dopo lo scale down per adattarsi a uno spazio in un layout con una larghezza di 400 pixel logici, che l'origine dell'immagine da 800 pixel ha una densità di pixel doppia, su un display con DPR di 2, sarà bello e nitido.

Primo piano di un petalo di fiore che mostra una disparità di densità.

Poiché una visualizzazione con un valore DPR di 1 non può utilizzare la maggiore densità di un'immagine, l'immagine verrà ridimensionata in modo che corrisponda al valore display; un'immagine ridimensionata sarà sufficiente. Su un display a bassa densità, un'immagine adatta a una maggiore densità i display avranno l'aspetto di qualsiasi altra immagine a bassa densità.

Come hai appreso in Immagini e rendimento, un utente con un display a bassa densità che visualizza un'origine immagine è stato ridotto a 400px avrà bisogno solo di un'origine con una larghezza intrinseca di 400px. Mentre un'immagine molto più grande funziona per tutti gli utenti visivamente, un'immagine l'origine di immagini ad alta risoluzione visualizzata su un display piccolo e a bassa densità sarà come qualsiasi altra immagine piccola e a bassa densità, ma risulta molto più lenta.

Come puoi intuire, i dispositivi mobili con un DPR pari a 1 sono molto rari, anche se è ancora comune in "computer" contesti di navigazione. In base ai dati condivise da Matt Hobbs, circa il 18% delle sessioni di navigazione su GOV.UK a partire da novembre 2022 riporta un DPR pari a 1. Sebbene le immagini ad alta densità svolgano esattamente come tali utenti potrebbero aspettarsi, hanno una larghezza di banda e un costo di elaborazione molto più elevati, ovvero per gli utenti che usano dispositivi meno recenti e meno potenti, hanno comunque probabilità di avere display a bassa densità.

L'uso di srcset garantisce che solo i dispositivi con display ad alta risoluzione ricevano origini di immagini sufficientemente grandi da apparire nitide, senza superare la stessa il costo della larghezza di banda insieme agli utenti con display a risoluzione più bassa.

L'attributo srcset identifica uno o più candidati separati da virgole al rendering di un'immagine. Ogni candidato è composto da due cose: un URL, proprio come lo useresti in src, e una sintassi che descrive l'origine dell'immagine. Ogni candidato in srcset è descritto dalla sua width intrinseca ("sintassi w") o dalla densità prevista ("sintassi x").

La sintassi x è un'abbreviazione di "questa origine è appropriata per una visualizzazione con questa densità"; il candidato seguito da 2x è Sono appropriati per un display con un DPR pari a 2.

<img src="low-density.jpg" srcset="double-density.jpg 2x" alt="...">

I browser che supportano srcset verranno presentati con due candidati: double-density.jpg, che 2x descrive come appropriato per i display con un DPR pari a 2 e low-density.jpg nell'attributo src: il candidato selezionato se non sono presenti dati più appropriati rilevato in srcset. Per i browser che non supportano srcset, l'attributo e i relativi contenuti verranno ignorati, ovvero i contenuti di src come di consueto.

È facile confondere i valori specificati nell'attributo srcset con le istruzioni. Questo 2x informa il browser che il file sorgente associato potrebbe essere adatto per l'utilizzo su un display con un DPR pari a 2, ovvero informazioni sulla fonte stessa. Non dice il browser come usare quella sorgente, comunica semplicemente al browser come potrebbe essere utilizzata la fonte. È una distinzione sottile ma importante: è un'immagine a doppia densità, non un'immagine da utilizzare su un display a doppia densità.

Differenza tra la sintassi "questa origine è appropriata per i display 2x" e una con la dicitura "Usa questa fonte sui display 2x" è lieve nella stampa, ma la densità dello schermo è solo uno dei tanti fattori collegati che il browser utilizza per decidere il candidato solo alcune delle quali conosci. Ad esempio, puoi determinare singolarmente che un utente ha attivato una che consente di risparmiare larghezza di banda nel browser tramite la query multimediale prefers-reduced-data e usarla per attivare sempre le immagini a bassa densità per gli utenti a prescindere dalla densità della visualizzazione, ma se non viene implementato in modo coerente da ogni sviluppatore su ogni sito web, non sarebbe di grande utilità per un utente. Potrebbero avere la loro preferenza rispettata su un sito e imbattersi in un muro di immagini che cancella la larghezza di banda nel prossimo.

L'algoritmo di selezione delle risorse volutamente vago utilizzato da srcset/sizes lascia spazio ai browser per decidere di selezionare la densità più bassa immagini con cali di larghezza di banda o in base a una preferenza di minimizzare l'utilizzo dei dati, senza che ci assumiamo la responsabilità di come, quando o quando una determinata soglia. Non ha senso assumersi delle responsabilità, e del lavoro aggiuntivo, se il browser è meglio attrezzato per gestire per te.

Descrivere le larghezze con w

srcset accetta un secondo tipo di descrittore per le origini delle immagini candidati. È molto più potente e, per i nostri scopi, molto più facile da capire. Invece di segnalare un candidato come avente le dimensioni appropriate per una determinata densità di visualizzazione, la sintassi w descrive la larghezza intrinseca di ogni origine candidata. Anche in questo caso, ogni candidato è identico, salvo per le dimensioni, la stessa contenuti, lo stesso ritaglio e le stesse proporzioni. In questo caso, però, vuoi che il browser dell'utente scelga tra due possibili opzioni: small.jpg, una sorgente con una larghezza intrinseca di 600 px, e large.jpg, una sorgente con una larghezza intrinseca di 1200 px.

srcset="small.jpg 600w, large.jpg 1200w"

Questa informazione non indica al browser cosa fare con queste informazioni, ma le fornisce solo un elenco di candidati per la visualizzazione dell'immagine. Prima che il browser possa decidere quale origine visualizzare, devi fornirgli qualche informazione in più: descrizione di come l'immagine verrà visualizzata sulla pagina. Per farlo, utilizza l'attributo sizes.

Descrizione dell'utilizzo con sizes

I browser sono incredibilmente performanti quando si tratta di trasferire le immagini. Le richieste di asset immagine verranno avviate a lungo prima delle richieste dei fogli di stile o di JavaScript, spesso anche prima che il markup sia stato completamente analizzato. Quando il browser effettua queste richieste, non ha informazioni sulla pagina stessa, a parte il markup: potrebbe non aver avviato richieste per i fogli di stile esterni, per non parlare di applicarli. Nel momento in cui il browser analizza il tuo markup e inizia a creare richieste, ha solo informazioni a livello di browser: le dimensioni dell'area visibile dell'utente, la densità dei pixel dello schermo preferenze utente e così via.

Questa informazione non ci dice nulla su come un'immagine deve essere visualizzata nel layout di pagina: non può neanche utilizzare l'area visibile come proxy per il limite superiore della dimensione img, poiché potrebbe occupare un contenitore a scorrimento orizzontale. Dobbiamo quindi fornire al browser queste informazioni e farlo utilizzando il markup. Questo è tutto ciò che potremo utilizzare per queste richieste.

Come nel caso di srcset, lo scopo di sizes è rendere disponibili le informazioni su un'immagine non appena il markup viene analizzato. Così come srcset è un'abbreviazione di "ecco i file di origine e le loro dimensioni intrinseche", l'attributo sizes è una forma abbreviata di "qui è la dimensione dell'immagine visualizzata nel layout." Il modo in cui descrivi l'immagine è relativo all'area visibile; di nuovo, l'area visibile sono le uniche informazioni di layout di cui il browser dispone quando viene effettuata la richiesta dell'immagine.

Potrebbe sembrare un po' contorto sulla stampa, ma nella pratica è molto più facile da capire:

<img
 sizes="80vw"
 srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
 src="fallback.jpg"
 alt="...">

In questo caso, questo valore sizes indica al browser che lo spazio nel nostro layout occupato da img ha una larghezza compresa tra 80vw e 80% di nell'area visibile. Ricorda che questa non è un'istruzione, ma una descrizione delle dimensioni dell'immagine nel layout di pagina. Non dice "fai in modo che che un'immagine occupa l'80% dell'area visibile", ma "questa immagine finirà per occupare l'80% dell'area visibile dopo il rendering della pagina".

In qualità di sviluppatore, il tuo lavoro è finito. Hai descritto con precisione un elenco di fonti candidate in srcset e la larghezza della tua immagine in sizes e, proprio come per la sintassi x in srcset, il resto dipende dal browser.

Ma nell'interesse di capire appieno come vengono utilizzate queste informazioni, prendiamoci un momento per analizzare le decisioni che il browser di un utente genera quando rileva questo markup:

Hai comunicato al browser che questa immagine occuperà l'80% dell'area visibile disponibile, quindi se eseguissimo il rendering di img su una dispositivo con un'area visibile di 1000 pixel di larghezza, questa immagine occuperà 800 pixel. Il browser prenderà quel valore e lo dividerà le larghezze di ciascuna delle origini immagine candidati che abbiamo specificato in srcset. L'origine più piccola ha una dimensione intrinseca di 600 pixel, così: 600÷800=0,75. La nostra immagine media ha una larghezza di 1200 pixel: 1200÷800=1,5. La nostra immagine più grande ha una larghezza di 2000 pixel: 2000÷800=2.5.

I risultati di questi calcoli (.75, 1.5 e 2.5) sono, di fatto, opzioni DPR specificamente personalizzate in base alla dell'area visibile. Poiché il browser dispone anche di informazioni sulla densità del display dell'utente, prende una serie di decisioni:

Con queste dimensioni dell'area visibile, il candidato small.jpg viene ignorato indipendentemente dalla densità di visualizzazione dell'utente, con un valore DPR calcolato più basso rispetto a 1, questa origine richiederebbe l'upscaling per qualsiasi utente, quindi non è appropriata. Su un dispositivo con un DPR pari a 1, medium.jpg fornisce corrispondenza più vicina: questa sorgente è appropriata per la visualizzazione a un DPR di 1.5, quindi è un po' più grande del necessario, ma ricorda che il downscaling un processo visivamente fluido. Su un dispositivo con DPR pari a 2,large.jpg è la corrispondenza più simile, quindi viene selezionato.

Se la stessa immagine viene visualizzata su un'area visibile di 600 pixel di larghezza, il risultato di tutto questo calcolo sarebbe completamente diverso: 80vw ora è 480px. Quando dividiamo le nostre fonti rispetto a questo, otteniamo 1.25, 2.5 e 4.1666666667. Con queste dimensioni dell'area visibile, verrà scelto small.jpg su 1 dispositivo, mentre medium.jpg corrisponderà su 2 dispositivi.

L'immagine sarà identica in tutti questi contesti di navigazione: tutti i nostri file sorgente sono esattamente uguali alle loro dimensioni, il rendering di ciascuno di questi elementi viene eseguito con la stessa intensità di visualizzazione dell'utente. Tuttavia, invece di mostrare large.jpg a ogni utente Per poter ospitare le aree visibili più grandi e le visualizzazioni a maggiore densità, agli utenti verrà sempre mostrato il candidato idoneo più piccolo. Utilizzando una sintassi descrittiva anziché una sintassi prescrittiva, non è necessario impostare manualmente i punti di interruzione e considerare le aree visibili future e DPR: è sufficiente fornire al browser informazioni e consentirgli di determinare le risposte per te.

Poiché il nostro valore sizes è relativo all'area visibile e completamente indipendente dal layout della pagina, aggiunge un livello di complicazione. È raro avere un'immagine che occupa solo una percentuale dell'area visibile, senza margini a larghezza fissa, spaziatura interna o influenza da altri elementi della pagina. Spesso dovrai esprimere la larghezza di un'immagine utilizzando una combinazione di unità. percentuali, em, px e così via.

Fortunatamente puoi utilizzare calc() qui: qualsiasi browser con supporto nativo per le immagini adattabili supporterà anche calc(), consentendoci di: Unità CSS combinate, ad esempio un'immagine che occupa l'intera larghezza dell'area visibile dell'utente, meno un margine di 1em su entrambi i lati:

<img
    sizes="calc(100vw-2em)"
    srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1600w, x-large.jpg 2400w"
    src="fallback.jpg"
    alt="...">

Descrizione dei punti di interruzione

Se hai trascorso molto tempo a lavorare con i layout adattabili, probabilmente avrai notato qualcosa che manca in questi esempi: è molto probabile che lo spazio occupato da un'immagine in un layout cambi tra i punti di interruzione del layout. In questo caso, è necessario per trasmettere al browser altri dettagli: sizes accetta un insieme di candidati separati da virgole per le dimensioni visualizzate immagine, proprio come srcset accetta candidati separati da virgole per le origini delle immagini. Queste condizioni utilizzano la diffusa sintassi delle query supporti. Questa sintassi è la prima corrispondenza: non appena viene trovata una condizione multimediale, il browser smette di analizzare l'attributo sizes e il valore specificato.

Supponiamo che tu abbia un'immagine pensata per occupare l'80% dell'area visibile, meno un em di spaziatura interna su entrambi i lati, nelle aree visibili superiori a 1200 px. aree visibili più piccole, occupa l'intera larghezza dell'area visibile.

  <img
     sizes="(min-width: 1200px) calc(80vw - 2em), 100vw"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

Se l'area visibile dell'utente è superiore a 1200 px, calc(80vw - 2em) descrive la larghezza dell'immagine nel nostro layout. Se La condizione (min-width: 1200px) non corrisponde e il browser passa al valore successivo. Poiché non esiste una specifica condizione multimediale associata a questo valore, viene utilizzata 100vw come impostazione predefinita. Se scrivi questo attributo sizes utilizzando max-width query supporti:

  <img
     sizes="(max-width: 1200px) 100vw, calc(80vw - 2em)"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

In parole semplici: "(max-width: 1200px) corrisponde? In caso contrario, vai avanti. Il valore successivo (calc(80vw - 2em)) non ha una condizione idonea, quindi questa è l'opzione selezionata.

Ora che hai fornito al browser tutte queste informazioni sull'elemento img: origini potenziali, larghezze intrinseche e come intendi presentare l'immagine all'utente: il browser utilizza un insieme fuzzy di regole per determinare l'azione da eseguire tali informazioni. Se sembra vago, beh, è perché lo è, per definizione. L'algoritmo di selezione della fonte codificato nel formato Le specifiche HTML sono esplicitamente vaghe sul modo in cui scegliere una fonte. Una volta che le sorgenti, i relativi descrittori e come l'immagine visualizzata è stata completamente analizzata, il browser è libero di fare quello che vuole e non puoi sapere con certezza quale sorgente scelta dal browser.

La sintassi "usa questa origine su un display ad alta risoluzione" sarebbe prevedibile, ma non risolverebbe il problema principale con immagini in un layout reattivo, risparmiando larghezza di banda dell'utente. La densità dei pixel di uno schermo è correlata solo tangenzialmente a internet velocità di connessione. Se utilizzi un laptop top di gamma, ma navighi sul web tramite una connessione a consumo, esegui il tethering al telefono o se la connessione Wi-Fi in aereo è instabile, ti consigliamo di disattivare le origini delle immagini ad alta risoluzione, indipendentemente la qualità del display.

Lasciare l'ultima parola al browser ci consente di migliorare le prestazioni di gran lunga superiori rispetto a quelli che potremmo gestire con una prescrizione a riga di comando. Ad esempio, nella maggior parte dei browser, un img che utilizza la sintassi srcset o sizes non disturberà mai la richiesta di un'origine con dimensioni inferiori rispetto a una già esistente nella cache del browser. Quale sarebbe lo scopo nel presentare una nuova richiesta per una fonte? che sarebbero identici, quando il browser è in grado di scalare senza problemi l'origine dell'immagine di cui già dispone? Ma se l'utente scala la propria l'area visibile fino al punto in cui è necessaria una nuova immagine per evitare l'upscaling, quella richiesta verrà comunque effettuata, quindi come previsto.

Tale mancanza di controllo esplicito può sembrare un po' spaventoso, ma poiché stai utilizzando file sorgente con contenuti identici non è più probabile presentare agli utenti un problema rispetto a quella di un'unica fonte src, indipendentemente dal fatto che le decisioni prese dal browser.

In uso: sizes e srcset

Si tratta di molte informazioni, sia per te, per il lettore sia per il browser. srcset e sizes sono entrambe sintassi dense, che descrive una quantità scioccante di informazioni in un numero relativamente ridotto di caratteri. Vale a dire, nel bene e nel male, per definizione: rendere queste sintassi meno precise e più facilmente analizzate da noi umani avrebbero potuto renderle più difficili da analizzare per un browser. La maggiore è la complessità aggiunta a una stringa, maggiore è il rischio di errori nell'analizzatore sintattico o differenze involontarie di comportamento da un browser all'altro. Ma c'è un lato positivo qui: una sintassi più facilmente letta dalle macchine è una sintassi più facile da scrivere da loro.

srcset è un esempio chiaro di automazione. È raro che tu crei a mano più versioni delle tue immagini per un di produzione, automatizzando invece il processo utilizzando un runner di attività come Gulp, un bundler come Webpack, CDN come Cloudinary o funzionalità già integrate nel CMS che preferisci. Abbiamo fornito informazioni sufficienti per generare le nostre fonti in primo luogo, un sistema avrà informazioni sufficienti per scriverle in un attributo srcset valido.

sizes è un po' più difficile da automatizzare. Come saprai, l'unico modo in cui un sistema può calcolare le dimensioni di un'immagine per il layout sottoposto a rendering consiste nell'eseguire il rendering del layout. Fortunatamente, sono apparsi una serie di strumenti per sviluppatori per astrarre il processo di scrittura a mano libera degli attributi sizes, con un'efficienza impensabile. respImageLint, ad esempio, è uno snippet di codice che verifica gli attributi sizes per l'accuratezza e fornire suggerimenti per migliorare. Il progetto Lazysizes viene compromesso una certa velocità per l'efficienza, posticipando le richieste di immagini fino a quando il layout non è stato stabilito, consentendo a JavaScript generare valori sizes per te. Se utilizzi un framework di rendering completamente lato client come React o Vue, è disponibile di soluzioni per la creazione e/o la generazione di attributi srcset e sizes, di cui parleremo più approfonditamente in CMS e framework.