Scherpe canvasweergave op hoge DPI- en Retina-schermen
Waarom canvasafbeeldingen er wazig uitzien op Retina-schermen en schermen met hoge DPI, en hoe u dit kunt oplossen met devicePixelRatio, inclusief verwerking van formaatwijzigingen en prestatielimieten.
Kern afhaalmaaltijd
Een canvas ziet er wazig uit op schermen met een hoge dichtheid als de pixelgrootte overeenkomt met de CSS-grootte, omdat elke canvaspixel over meerdere fysieke pixels is uitgerekt. Los dit probleem op door de canvasbreedte en -hoogte in te stellen op de CSS-grootte vermenigvuldigd met window.devicePixelRatio, waarbij de CSS-grootte ongewijzigd blijft, en de tekencontext met dezelfde verhouding te schalen. Update het door het formaat te wijzigen en in te zoomen.
Waarom doeken vervagen
MDN beschrijft devicePixelRatio als de verhouding tussen fysieke pixels en CSS-pixels op het huidige scherm: 1 op een klassiek 96 DPI-scherm en 2 op typische HiDPI- of Retina-schermen. Een canvas heeft zijn eigen pixelbuffer. Als die buffer 300 bij 150 pixels is, maar wordt weergegeven met 300 bij 150 CSS pixels op een scherm met een verhouding van 2, rekt de browser deze uit over 600 bij 300 fysieke pixels, wat er zacht uitziet.
De oplossing
De aanbevolen aanpak van MDN is om devicePixelRatio te gebruiken om de extra pixeldichtheid toe te voegen: stel de canvasbuffer in op de CSS-grootte maal de verhouding, houd de CSS-grootte hetzelfde en schaal de 2D-context zodat uw tekencode CSS-pixels kan blijven gebruiken. Zie de onderstaande code.
Behandel formaatwijzigingen en zoom
De verhouding verandert wanneer een venster naar een andere monitor wordt verplaatst of de gebruiker op de pagina zoomt. Bereken opnieuw bij het wijzigen van het formaat en luister naar wijzigingen in de resolutiemediaquery, zodat u opnieuw kunt tekenen met de nieuwe dichtheid. Een ResizeObserver op het canvaselement is een betrouwbare manier om de grootte synchroon te houden met de lay-out.
Prestatielimieten
Als u de verhouding verdubbelt, verviervoudigt u het aantal te tekenen pixels. Op zeer grote doeken of goedkope apparaten kunt u de verhouding beperken tot bijvoorbeeld 2, en de frametijden meten. Animatieruntimes die naar canvas tekenen, zoals de canvas-renderer van Lottie, Rive en ExodeUI, verzorgen deze schaalvergroting voor u, maar dezelfde afweging is van toepassing.
Productie-implementatiefragment
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
}Toets Principles in om te onthouden
- devicePixelRatio = fysieke pixels per CSS-pixel (2 op typische Retina-schermen)
- Stel canvas.width/height in op CSS size × ratio; behoud de CSS-grootte
- Schaal de context zodat tekencode CSS-pixels gebruikt
- Herbereken bij formaat wijzigen, monitorwijziging en zoom
- Beperk de verhouding op grote doeken voor prestaties
Veelgestelde vragen
Bronnen
- Window: devicePixelRatio property — MDN Webdocumenten