Terävä kankaan renderöinti High-DPI- ja Retina-näytöillä
Miksi canvas-grafiikka näyttää epäselvältä Retina- ja korkean DPI-näytöillä ja kuinka korjata se devicePixelRatiolla, mukaan lukien koon muuttamisen käsittely ja suorituskykyrajoitukset.
Core Takeaway
Kanvas näyttää epäselvältä tiheällä näytöllä, kun sen pikselikoko vastaa sen CSS-kokoa, koska jokainen kangaspikseli on venytetty useille fyysisille pikseleille. Korjaa se asettamalla kankaan leveydeksi ja korkeudeksi CSS-koko kerrottuna window.devicePixelRatio-suhteella, pitämällä CSS-koko ennallaan ja skaalaamalla piirustuskontekstia samalla suhteella. Päivitä se kokoa ja zoomaamalla.
Miksi kankaat sumentuvat
MDN kuvaa laitteen pikselisuhdetta fyysisten pikselien ja CSS-pikseleiden suhteena nykyisellä näytöllä: 1 perinteisellä 96 DPI-näytöllä ja 2 tyypillisillä HiDPI- tai Retina-näytöillä. Kanvasilla on oma pikselipuskuri. Jos puskuri on 300 x 150 pikseliä, mutta se näkyy 300 x 150 CSS-pikselinä näytöllä, jonka suhde on 2, selain venyttää sen yli 600 x 300 fyysisen pikselin, mikä näyttää pehmeältä.
Korjaus
MDN:n suosittelema lähestymistapa on käyttää devicePixelRatioa ylimääräisen pikselitiheyden lisäämiseen: aseta kanvaspuskuriksi CSS-koko kertaa suhde, pidä CSS-koko samana ja skaalaa 2D-konteksti, jotta piirustuskoodisi voi käyttää CSS-pikseleitä. Katso alla oleva koodi.
Kahva muuttaa kokoa ja zoomaa
Suhde muuttuu, kun ikkuna siirtyy toiseen näyttöön tai käyttäjä zoomaa sivua. Laske uudelleen kokoa muuttaessa ja kuuntele tarkkuuden mediakyselyn muutoksia, jotta voit piirtää uudelleen uudella tiheydellä. ResizeObserver canvas-elementissä on luotettava tapa pitää koko synkronoituna asettelun kanssa.
Suorituskyvyn rajoitukset
Suhteen kaksinkertaistaminen nelinkertaistaa piirrettävät pikselit. Suurilla kankailla tai halvemmilla laitteilla rajoita suhde, esimerkiksi 2, ja mittaa kuva-ajat. Kanvaan piirrettävät animaation suoritusajat, kuten Lottie:n kanvas-renderöijä, Rive ja ExodeUI, hoitavat tämän skaalan puolestasi, mutta sama kompromissi koskee.
Tuotannon toteutuskatkelma
function setupCanvas(canvas) {
const ctx = canvas.getContext('2d');
function resize() {
const ratio = Math.min(window.devicePixelRatio || 1, 2); // cap for performance
const { width, height } = canvas.getBoundingClientRect(); // CSS size
canvas.width = Math.round(width * ratio); // backing store in device pixels
canvas.height = Math.round(height * ratio);
ctx.setTransform(ratio, 0, 0, ratio, 0, 0); // draw in CSS pixels
draw(ctx, width, height);
}
new ResizeObserver(resize).observe(canvas);
resize();
}
function draw(ctx, width, height) {
ctx.clearRect(0, 0, width, height);
ctx.lineWidth = 1;
ctx.strokeRect(0.5, 0.5, width - 1, height - 1); // crisp 1px border
}Avain Principles muistettavat
- devicePixelRatio = fyysiset pikselit CSS-pikseliä kohti (2 tavallisilla Retina-näytöillä)
- Aseta canvas.width/height arvoon CSS koko × -suhde; Säilytä koko CSS
- Skaalaa konteksti niin, että piirroskoodi käyttää CSS pikseleitä
- Laske koon uudelleen, seuraa muutosta ja zoomaa
- Korjaa suhde suurilla kankailla suorituskyvyn saavuttamiseksi
Usein kysytyt kysymykset
Lähteet
- Window: devicePixelRatio property — MDN Web Docs