Redare clară a pânzei pe ecrane cu DPI ridicat și Retina
De ce grafica canvas pare neclară pe ecranele Retina și cu DPI ridicat și cum să o remediați cu devicePixelRatio, inclusiv gestionarea redimensionării și limitele de performanță.
Core Takeaway
O pânză pare neclară pe ecranele cu densitate mare atunci când dimensiunea pixelului său se potrivește cu dimensiunea lui CSS, deoarece fiecare pixel al pânzei este întins pe mai mulți pixeli fizici. Remediați-l setând lățimea și înălțimea pânzei la dimensiunea CSS înmulțită cu window.devicePixelRatio, păstrând dimensiunea CSS neschimbată și scalând contextul desenului cu același raport. Actualizați-l la redimensionare și mărire.
De ce pânzele se estompează
MDN descrie devicePixelRatio ca fiind raportul dintre pixelii fizici și pixelii CSS de pe afișajul curent: 1 pe un ecran clasic de 96 DPI și 2 pe ecrane tipice HiDPI sau Retina. O pânză are propriul buffer de pixeli. Dacă acel buffer este de 300 pe 150 de pixeli, dar este afișat la 300 pe 150 de pixeli CSS pe un ecran cu un raport de 2, browserul îl întinde pe 600 pe 300 de pixeli fizici, ceea ce pare moale.
Remedierea
Abordarea recomandată de MDN este să utilizați devicePixelRatio pentru a adăuga o densitate suplimentară de pixeli: setați tamponul canvas la dimensiunea CSS ori raportul, păstrați dimensiunea CSS la fel și scalați contextul 2D, astfel încât codul dvs. de desen să poată folosi în continuare pixelii CSS. Vezi codul de mai jos.
Gestionați redimensionările și măriți
Raportul se modifică atunci când o fereastră trece pe alt monitor sau când utilizatorul mărește pagina. Recalculați la redimensionare și ascultați modificările la interogarea media de rezoluție, astfel încât să puteți redesena la noua densitate. Un ResizeObserver pe elementul canvas este o modalitate fiabilă de a menține dimensiunea sincronizată cu aspectul.
Limite de performanță
Dublarea raportului face de patru ori pixelii de desenat. Pe pânzele foarte mari sau pe dispozitivele low-end, limitați raportul, de exemplu la 2, și măsurați timpii cadrelor. Timpurile de execuție ale animației care se apropie de pânză, cum ar fi redarea canvasului Lottie, Rive și ExodeUI, gestionează această scalare pentru dvs., dar se aplică același compromis.
Fragment de implementare a producției
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
}Cheie Principles de reținut
- devicePixelRatio = pixeli fizici per pixel CSS (2 pe ecranele tipice Retina)
- Setați canvas.width/height la CSS dimensiune × raport; păstrați dimensiunea CSS
- Scalați contextul astfel încât codul de desen să folosească pixeli CSS
- Recalculați la redimensionare, monitorizați modificarea și măriți
- Limitați raportul pe pânze mari pentru performanță
Întrebări frecvente
Surse
- Window: devicePixelRatio property — MDN Web Docs