高 DPI および Retina スクリーンでのシャープなキャンバス レンダリング
Retina や高 DPI 画面でキャンバスのグラフィックがぼやけて見える理由と、devicePixelRatio を使用してそれを修正する方法 (サイズ変更の処理やパフォーマンスの制限など)。
核心
キャンバスのピクセル サイズが CSS サイズと一致すると、高密度画面ではキャンバスがぼやけて見えます。これは、キャンバスの各ピクセルが複数の物理ピクセルにまたがるためです。この問題を修正するには、キャンバスの幅と高さを CSS サイズに window.devicePixelRatio を乗算した値に設定し、CSS サイズを変更せずに維持し、描画コンテキストを同じ比率でスケーリングします。サイズ変更とズーム時に更新します。
キャンバスがぼやける理由
MDN は、現在のディスプレイ上の CSS ピクセルに対する物理ピクセルの比率として devicePixelRatio を記述します。クラシック 96 DPI 画面では 1、一般的な HiDPI または Retina 画面では 2 です。キャンバスには独自のピクセル バッファーがあります。そのバッファーが 300 x 150 ピクセルであるが、比率 2 の画面上で 300 x 150 CSS ピクセルで表示される場合、ブラウザーはバッファーを 600 x 300 物理ピクセルを超えて拡大し、柔らかく見えます。
修正
MDN が推奨するアプローチは、devicePixelRatio を使用して追加のピクセル密度を追加することです。つまり、キャンバス バッファーを CSS サイズに比率を掛けた値に設定し、CSS サイズを同じに保ち、描画コードが CSS ピクセルを使用し続けることができるように 2D コンテキストをスケールします。以下のコードを参照してください。
サイズ変更とズームをハンドルします
ウィンドウが別のモニターに移動するか、ユーザーがページをズームすると、比率が変わります。サイズ変更時に再計算し、解像度メディア クエリの変更をリッスンして、新しい密度で再描画できるようにします。 Canvas 要素上の ResizeObserver は、サイズをレイアウトと同期させるための信頼できる方法です。
パフォーマンスの限界
比率を 2 倍にすると、描画するピクセルが 4 倍になります。非常に大きなキャンバスやローエンドのデバイスでは、比率をたとえば 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
}覚えておきたい原則
- devicePixelRatio = CSS ピクセルあたりの物理ピクセル (一般的な Retina スクリーンでは 2)
- Canvas.width/height を CSS サイズ × 比率に設定します。 CSS サイズを維持する
- 描画コードが CSS ピクセルを使用するようにコンテキストをスケールします。
- サイズ変更、モニター変更、ズーム時に再計算します
- パフォーマンスのために大きなキャンバスでは比率を制限する
よくある質問
出典
- Window: devicePixelRatio property — MDN ウェブ ドキュメント