Scharfe Leinwandwiedergabe auf High-DPI- und Retina-Bildschirmen
Warum Leinwandgrafiken auf Retina- und High-DPI-Bildschirmen verschwommen aussehen und wie man das Problem mit devicePixelRatio behebt, einschließlich Größenänderungsbehandlung und Leistungsbeschränkungen.
Kernaussage
Eine Leinwand sieht auf Bildschirmen mit hoher Dichte unscharf aus, wenn ihre Pixelgröße mit ihrer CSS-Größe übereinstimmt, da jedes Leinwandpixel über mehrere physische Pixel gestreckt ist. Beheben Sie das Problem, indem Sie die Leinwandbreite und -höhe auf die CSS-Größe multipliziert mit window.devicePixelRatio festlegen, die CSS-Größe unverändert lassen und den Zeichnungskontext im gleichen Verhältnis skalieren. Aktualisieren Sie es bei Größenänderung und Zoom.
Warum Leinwände verschwimmen
MDN beschreibt devicePixelRatio als das Verhältnis von physischen Pixeln zu CSS Pixeln auf dem aktuellen Display: 1 auf einem klassischen 96-DPI-Bildschirm und 2 auf typischen HiDPI- oder Retina-Bildschirmen. Eine Leinwand verfügt über einen eigenen Pixelpuffer. Wenn dieser Puffer 300 x 150 Pixel groß ist, aber auf einem Bildschirm mit einem Verhältnis von 2 mit 300 x 150 CSS Pixeln angezeigt wird, dehnt der Browser ihn über 600 x 300 physische Pixel aus, was weich aussieht.
Die Lösung
Der von MDN empfohlene Ansatz besteht darin, devicePixelRatio zu verwenden, um die zusätzliche Pixeldichte hinzuzufügen: Stellen Sie den Canvas-Puffer auf die CSS-Größe mal das Verhältnis ein, behalten Sie die CSS-Größe bei und skalieren Sie den 2D-Kontext, damit Ihr Zeichencode weiterhin CSS-Pixel verwenden kann. Siehe den Code unten.
Behandeln Sie Größenänderungen und Zoom
Das Verhältnis ändert sich, wenn ein Fenster auf einen anderen Monitor verschoben wird oder der Benutzer die Seite zoomt. Führen Sie bei der Größenänderung eine Neuberechnung durch und achten Sie auf Änderungen an der Medienabfrage für die Auflösung, damit Sie mit der neuen Dichte neu zeichnen können. Ein ResizeObserver auf dem Canvas-Element ist eine zuverlässige Möglichkeit, die Größe mit dem Layout synchron zu halten.
Leistungsgrenzen
Durch Verdoppelung des Verhältnisses werden die zu zeichnenden Pixel vervierfacht. Begrenzen Sie auf sehr großen Leinwänden oder Low-End-Geräten das Verhältnis, beispielsweise auf 2, und messen Sie die Bildzeiten. Animationslaufzeiten, die auf Canvas zeichnen, wie z. B. der Canvas-Renderer von Lottie, Rive und ExodeUI, übernehmen diese Skalierung für Sie, es gilt jedoch der gleiche Kompromiss.
Produktionsnahes Implementierungsbeispiel
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
}Wichtige Prinzipien
- devicePixelRatio = physische Pixel pro CSS Pixel (2 auf typischen Retina-Bildschirmen)
- Setzen Sie „canvas.width/height“ auf CSS Größe × Verhältnis; Behalten Sie die CSS-Größe bei
- Skalieren Sie den Kontext so, dass der Zeichencode CSS Pixel verwendet
- Neuberechnung bei Größenänderung, Monitorwechsel und Zoom
- Begrenzen Sie das Verhältnis auf großen Leinwänden aus Leistungsgründen
Häufige Fragen
Quellen
- Window: devicePixelRatio property — MDN Webdokumente