Om een responsieve afbeeldingsvariant te voorspellen, zet je sizes om in een CSS-breedte, vermenigvuldig je die met de apparaatpixelverhouding en vergelijk je die doelbreedte met srcset. Controleer vervolgens de geselecteerde URL: browsers bepalen uiteindelijk zelf welke variant ze kiezen.

Deze handleiding is bedoeld voor iemand die een zelf geschreven webpagina controleert in Chrome op een Mac. Je berekent waarschijnlijke varianten, test een lokale pagina met zelfgemaakte abstracte vormen en maakt onderscheid tussen een misleidende grootteverklaring en normaal selectiegedrag van de browser.

Wat vertellen sizes, srcset en DPR elk aan de browser?

Bij breedtedescriptors vermeldt srcset de beschikbare bronnen en hun intrinsieke pixelbreedtes. Een descriptor zoals 800w moet een afbeelding beschrijven die daadwerkelijk 800 pixels breed is. Houd breedtedescriptors en dichtheidsdescriptors zoals 2x in aparte bronverzamelingen; ze combineren in één srcset is ongeldig. De referentie voor het image-element van MDN beschrijft deze vereisten.

sizes geeft de bedoelde weergavebreedte voor bronselectie op. De browser evalueert de voorwaarden op volgorde en gebruikt de eerste overeenkomstige regel. Plaats de onvoorwaardelijke terugvaloptie als laatste. Bronformaten accepteren lengtes zoals 480px, 50vw en calc(100vw - 32px); percentagewaarden zoals 50% zijn geen geldige slotbreedtes. Responsieve afbeeldingen gebruiken in HTML

CSS bepaalt de weergegeven afmetingen. Beschouw je CSS en expliciete lijst met sizes als twee beschrijvingen die op elkaar moeten zijn afgestemd: de ene bepaalt de lay-out, de andere geeft informatie voor bronselectie. Alleen sizes wijzigen voert de bijbehorende lay-out niet uit. HTML-standaard: sizes-attributen

DPR beschrijft de verhouding tussen fysieke pixels en CSS-pixels. Lees window.devicePixelRatio uit in plaats van een waarde af te leiden uit het Mac-model of de beeldschermnaam. Paginazoom kan deze waarde wijzigen, net als het verplaatsen van een venster tussen beeldschermen. Pinch-zoom verandert DPR niet. MDN: Window.devicePixelRatio

Hoe bereken je de waarschijnlijke variant?

Gebruik voor een opgeloste bronbreedte S, een bronbreedte W en DPR D deze twee gelijkwaardige berekeningen:

Doelbreedte bron = S × D
Effectieve dichtheid variant = W ÷ S

Het HTML-verwerkingsmodel normaliseert breedtedescriptors door de breedte van elke variant te delen door de bronbreedte. MDN beschrijft de praktische voorspelling ook als het vermenigvuldigen van de groottehint met DPR. HTML-standaard: bron-dichtheden normaliseren, MDN: HTMLImageElement.sizes

Kies als bruikbare schatting de kleinste variant waarvan de breedte minstens gelijk is aan de doelbreedte. Als alle varianten smaller zijn dan het doel, noteer dan de grootste beschikbare variant en het tekort. Dit is een expliciete voorspellingsregel voor de onderstaande oefeningen. De HTML-standaard laat de uiteindelijke selectie over aan de implementatie; niet elke browser hoeft deze afrondingsregel te volgen. HTML-standaard: een afbeeldingsbron selecteren

Bekijk deze lijst met bronbreedtes, in combinatie met varianten van 480w, 800w, 1200w en 1440w:

(max-width: 600px) calc(100vw - 32px),
(max-width: 1200px) calc(50vw - 32px),
568px

100vw staat voor de viewportbreedte en 50vw voor de helft daarvan. Deze eenheden verwijzen naar de viewport, niet naar de bovenliggende container van de afbeelding. CSS-waarden en eenheden: lengtes als percentage van de viewport

De volgende tabel bevat illustratieve berekeningen, geen vastgelegde browserresultaten. De laatste kolom past de genoemde voorspellingsregel toe.

Viewportbreedte, CSS-px DPR Opgeloste bronbreedte, CSS-px Doelbreedte bron, px Voorspelde variant
600 1 568 568 800w
600 2 568 1,136 1200w
601 2 268.5 537 800w
1,000 1 468 468 480w
1,000 2 468 936 1200w
1,400 2 568 1,136 1200w
1,400 3 568 1,704 1440w, onder doel

De overgang van 600 naar 601 CSS-pixels is bijzonder nuttig om te testen. Bij 600 levert de eerste voorwaarde een bijna schermbrede slotbreedte op. Bij 601 levert de tweede voorwaarde een veel smallere slotbreedte op. Een bredere viewport leidt daardoor tot een kleinere doelafbeelding. Dat volgt rechtstreeks uit deze vastgelegde lay-outafspraken.

Bij een viewport van 1.000 pixels en DPR 2 is de bronbreedte 468 CSS-pixels. De vier varianten hebben effectieve dichtheden van ongeveer 1.03x, 1.71x, 2.56x en 3.08x. Volgens onze voorspellingsregel is de 1200w-variant de eerste die de doeldichtheid van 2x overschrijdt.

Hoe bouw je een volledige lokale test?

Gebruik een Mac waarop Chrome en Python 3.8 of hoger zijn geïnstalleerd. Sla het volgende volledige programma met de naam responsive_demo.py op in een gewone teksteditor. Het maakt vier PNG-bestanden met dezelfde rechthoek en cirkel, schrijft een bijbehorende pagina en start een lokale server. Alle gegenereerde bestanden komen in een nieuwe tijdelijke map terecht, zodat bestaande bestanden niet worden overschreven.

De pagina gebruikt bewust een afbeelding die meteen wordt geladen en expliciete bronbreedtes. De CSS-breedtes komen overeen met de drie varianten uit de berekeningstabel. De server gebruikt Python’s gedocumenteerde ThreadingHTTPServer en een aanvraaghandler die op mappen is gebaseerd. Python: http.server

from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
import struct
import tempfile
import zlib


def chunk(kind, data):
    return (
        struct.pack("!I", len(data)) + kind + data
        + struct.pack("!I", zlib.crc32(kind + data))
    )


def make_png(width):
    height = width // 2
    rows = []
    for y in range(height):
        row = bytearray()
        for x in range(width):
            color = (242, 244, 248)
            if width // 8 <= x < width // 2 and height // 4 <= y < 3 * height // 4:
                color = (70, 100, 160)
            if (x - 3 * width // 4) ** 2 + (y - height // 2) ** 2 < (height // 5) ** 2:
                color = (220, 160, 60)
            row.extend(color)
        rows.append(b"\x00" + row)
    header = struct.pack("!IIBBBBB", width, height, 8, 2, 0, 0, 0)
    return (
        b"\x89PNG\r\n\x1a\n"
        + chunk(b"IHDR", header)
        + chunk(b"IDAT", zlib.compress(b"".join(rows)))
        + chunk(b"IEND", b"")
    )


page = '''<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Shape A candidate test</title>
<style>
  body { margin: 0; padding: 16px; font-family: sans-serif; }
  #shape { display: block; width: calc(100vw - 32px); height: auto; }
  @media (min-width: 600.01px) {
    #shape { width: calc(50vw - 32px); }
  }
  @media (min-width: 1200.01px) {
    #shape { width: 568px; }
  }
</style>
<p>Shape A: rectangle and circle</p>
<img id="shape" src="shape-a-800.png" width="1440" height="720"
     alt="Blue rectangle and amber circle on a pale background"
     srcset="shape-a-480.png 480w, shape-a-800.png 800w,
             shape-a-1200.png 1200w, shape-a-1440.png 1440w"
     sizes="(max-width: 600px) calc(100vw - 32px),
            (max-width: 1200px) calc(50vw - 32px), 568px">
</html>
'''

root = Path(tempfile.mkdtemp(prefix="responsive-shapes-"))
for width in (480, 800, 1200, 1440):
    (root / f"shape-a-{width}.png").write_bytes(make_png(width))
(root / "index.html").write_text(page, encoding="utf-8")
handler = partial(SimpleHTTPRequestHandler, directory=str(root))
with ThreadingHTTPServer(("127.0.0.1", 0), handler) as server:
    print(f"Files: {root}", flush=True)
    print(f"Open http://127.0.0.1:{server.server_port}/", flush=True)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass

Ga in Terminal naar de map waarin je het programma hebt opgeslagen en voer het volgende uit:

python3 responsive_demo.py

Open het lokale adres dat het programma weergeeft in Chrome. Laat Terminal tijdens de test openstaan; druk daarna op Control+C om de server te stoppen. Het programma vraagt om een beschikbare poort. Gebruik dus het weergegeven adres in plaats van uit te gaan van een vast poortnummer.

De CSS-drempels gebruiken 600.01px en 1200.01px voor de gehele viewportbreedtes van de desktoptest. Test de exacte gehele waarden uit de tabel. Stem bij aanpassing van het voorbeeld aan een productielay-out je werkelijke CSS-breakpoints en bronbreedtevoorwaarden op elkaar af, inclusief gedrag bij fractionele breedtes.

Hoe controleer je de selectie in Chrome op een Mac?

Klik met de rechtermuisknop op de vorm en selecteer Inspecteren om DevTools te openen met het image-element geselecteerd. Chrome beschrijft dit als een directe manier om een element en de toegepaste stijlen ervan te inspecteren. Chrome DevTools openen

  1. Klik op Apparaatwerkbalk in- of uitschakelen en kies Responsive onder Afmetingen.
  2. Stel de viewportbreedte in op 1000.
  3. Kies Apparaatpixelverhouding toevoegen in het menu Meer opties van de werkbalk en selecteer vervolgens DPR 2.
  4. Gebruik zo nodig Meer opties, kies Apparaattype toevoegen en selecteer Desktop.
  5. Open Netwerk, schakel Cache uitschakelen in en laad de pagina opnieuw met DevTools geopend.
  6. Zoek de aangevraagde afbeelding door te filteren op shape-a.

Chrome beschrijft de bediening voor viewport, DPR en apparaattype in de gids voor Apparaatmodus en de cachebediening in de referentie voor netwerkfuncties.

Voer na het laden van de pagina dit volledige diagnostische fragment uit in de Console van DevTools:

(() => {
  const image = document.getElementById("shape");
  if (!image) throw new Error("The Shape A image was not found.");

  const viewport = window.innerWidth;
  const slot = window.matchMedia("(max-width: 600px)").matches
    ? viewport - 32
    : window.matchMedia("(max-width: 1200px)").matches
      ? viewport / 2 - 32
      : 568;

  console.table({
    viewportCSSPixels: viewport,
    devicePixelRatio: window.devicePixelRatio,
    declaredSlotCSSPixels: slot,
    renderedWidthCSSPixels: image.getBoundingClientRect().width,
    targetResourcePixels: slot * window.devicePixelRatio,
    selectedURL: image.currentSrc,
    naturalWidthCSSPixels: image.naturalWidth
  });
})();

Dit fragment berekent de opgegeven slotbreedte specifiek voor deze demo; het is geen algemene parser voor sizes. Vergelijk de opgegeven slotbreedte met de weergegeven breedte en vergelijk vervolgens de geselecteerde bestandsnaam met de voorspellingstabel.

currentSrc geeft de volledige URL weer die de browser heeft geselecteerd. Daarmee staat niet vast dat de bron succesvol is geladen; controleer daarom ook het afbeeldingsverzoek en het weergegeven resultaat. Als je src uitleest, zie je alleen het terugvalattribuut in dit voorbeeld. MDN: HTMLImageElement.currentSrc

Lees naturalWidth niet als de gecodeerde PNG-breedte. Het is een voor dichtheid gecorrigeerde breedte in CSS-pixels. In deze gecontroleerde testgegevens geven de afmetingen en bestandsnaam van het gegenereerde bestand de bronbreedte aan. MDN: HTMLImageElement.naturalWidth

Herhaal de test bij 600 en 601 CSS-pixels en laad de pagina voor elke situatie opnieuw. Noteer de viewport, de gerapporteerde DPR, de slotbreedte, de geselecteerde bestandsnaam en of het verzoek is voltooid. Zo kun je een herhaalbaar resultaat na een nieuwe laadbeurt onderscheiden van een reeks keuzes tijdens het wijzigen van de grootte van een al geladen pagina.

Wat moet je controleren als de voorspelling afwijkt?

Controleer de invoer voordat je de variantenlijst wijzigt:

  1. Controleer de descriptorklasse. Pas bij dichtheidsdescriptors zoals 1x en 2x de berekening voor breedtedescriptors niet toe via sizes.
  2. Controleer de eerste overeenkomstige voorwaarde. Een eerdere overeenkomstige voorwaarde heeft voorrang op latere vermeldingen.
  3. Vergelijk de opgegeven en weergegeven breedte. Een opgave van 100vw kan een bredere slotbreedte aangeven dan de CSS toestaat voor een smalle kolom.
  4. Lees de werkelijke DPR uit. Controleer die opnieuw na paginazoom of een wijziging van beeldscherm.
  5. Vergelijk gecontroleerde laadbeurten. Houd viewport, DPR en laadomstandigheden hetzelfde tijdens het herhalen van de test.
  6. Controleer of de bron beschikbaar is. Een geselecteerde URL en een afbeelding die succesvol wordt weergegeven zijn twee afzonderlijke waarnemingen.

Ontbrekende of onbruikbare expliciete bronbreedtes kunnen ertoe leiden dat de HTML-parser de standaardwaarde 100vw gebruikt. Dat kan een verrassend groot doel voor een smalle afbeelding verklaren. Geef een nauwkeurige lijst met bronbreedtes op in plaats van aan te nemen dat de browser de bedoelde kolombreedte uit je CSS afleidt. HTML-standaard: een sizes-attribuut parsen

Als een pagina een picture-element gebruikt, bepaal dan eerst welke bronvertakking van toepassing is voordat je dichtheden berekent. De bronselectie vindt plaats vóór de keuze uit de varianten van die vertakking; verschillende uitsneden horen bij dit mechanisme voor art direction. Responsieve afbeeldingen gebruiken in HTML

Welke ondersteuningsbeperkingen zijn van invloed op deze methode?

MDN geeft aan dat de eigenschap sizes breed wordt ondersteund door browsers, maar dat betekent niet dat elk nieuwer trefwoord even goed wordt ondersteund. De expliciete voorwaarden die hier worden gebruikt, vormen een eenvoudige test zonder afhankelijk te zijn van automatische groottebepaling. MDN: HTMLImageElement.sizes

sizes="auto" is geldig voor lazy-loaded afbeeldingen en maakt het mogelijk om bij selectie de lay-outbreedte ervan te gebruiken. Als je dit toepast, behoud dan expliciete terugvalbreedtes na auto voor browsers die dit trefwoord niet ondersteunen, en geef afbeeldingsafmetingen op. Vervang het niet in dit voorbeeld waarin de afbeelding meteen wordt geladen zonder die randvoorwaarden aan te passen. De referentie voor het image-element van MDN

De apparaatbediening van Chrome test het gedrag van Chrome onder gesimuleerde omstandigheden. Ze tonen niet hoe Safari of Firefox varianten kiest. Chrome beschrijft Apparaatmodus bovendien als een benadering. Test daarom rechtstreeks in een andere browser als het gedrag daarvan van belang is. Chrome DevTools: beperkingen van Apparaatmodus

Een bruikbare voorspelling eindigt met een controleerbare registratie: de toepasselijke bronverzameling, de opgeloste bronbreedte, de gerapporteerde DPR, het berekende doel en de waargenomen currentSrc. Als die invoer overeenkomt met de vastgelegde lay-out, kun je de bronkeuze helder uitleggen en bepalen of de volgende wijziging in CSS, sizes of de beschikbare afbeeldingsbreedtes thuishoort.

Bronnen