高 DPI 和視網膜螢幕上的清晰畫布渲染
為什麼畫布圖形在 Retina 和高 DPI 螢幕上看起來模糊,以及如何使用 devicePixelRatio 修復它,包括調整大小處理和效能限制。
核心要點
當畫布的像素大小與其 CSS 大小相符時,畫佈在高密度螢幕上看起來很模糊,因為每個畫布像素都拉伸到多個物理像素。透過將畫布寬度和高度設定為 CSS 大小乘以 window.devicePixelRatio 來修復此問題,保持 CSS 大小不變,並以相同比例縮放繪圖上下文。更新它以調整大小和縮放。
為什麼畫布模糊
MDN 將 devicePixelRatio 描述為目前顯示器上實體像素與 CSS 像素的比率:在經典 96 DPI 螢幕上為 1,在典型 HiDPI 或 Retina 螢幕上為 2。畫布有自己的像素緩衝區。如果該緩衝區為 300 x 150 像素,但在比例為 2 的螢幕上以 300 x 150 CSS 像素顯示,則瀏覽器會將其拉伸超過 600 x 300 實體像素,這看起來很柔和。
修復
MDN 的建議方法是使用 devicePixelRatio 來添加額外的像素密度:將畫布緩衝區設定為 CSS 大小乘以比率,保持 CSS 大小相同,並縮放 2D 上下文,以便您的繪圖程式碼可以繼續使用 CSS 像素。請參閱下面的程式碼。
處理調整大小和縮放
當視窗移動到另一個監視器或使用者縮放頁面時,比率會改變。在調整大小時重新計算,並偵聽解析度媒體查詢的更改,以便您可以以新的密度重新繪製。 canvas 元素上的 ResizeObserver 是保持尺寸與佈局同步的可靠方法。
性能限制
比率加倍會使要繪製的像素增加四倍。在非常大的畫布或低端設備上,將比率限制為 2,並測量幀時間。繪製到畫布的動畫運行時(例如 Lottie 的畫布渲染器、Rive 和 ExodeUI)可以為您處理這種縮放,但相同的權衡也適用。
生產實施片段
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
}要記住的關鍵 Principle
- devicePixelRatio = 每 CSS 像素的實體像素(在典型的 Retina 螢幕上為 2)
- 設定canvas.width/height為CSS尺寸×比例;維持CSS尺寸
- 縮放上下文,以便繪圖程式碼使用 CSS 像素
- 重新計算調整大小、監視變更和縮放
- 限制大畫布上的比例以提高性能
常見問題解答
來源
- Window: devicePixelRatio property — MDN 網路文檔