高 DPI キャンバス

はじめに

美しい HiDPI 画面により、すべてが滑らかで見やすく表示されます。ただし、デベロッパーには新たな課題も提示されます。この記事では、HiDPI 画面のコンテキストでキャンバスに画像を描画する際の独自の課題について説明します。

devicePixelRatio プロパティ

初めから始めましょう。HiDPI 画面が登場する前は、ピクセルはピクセルでした(ズームとスケーリングを無視した場合)。それだけであり、特に変更する必要はありませんでした。幅を 100 ピクセルに設定すれば、それだけで完了でした。その後、最初の HiDPI モバイル ハンドセットが登場し、window オブジェクトに謎めいた devicePixelRatio プロパティが追加され、メディアクエリで使用できるようになりました。このプロパティにより、たとえば CSS のピクセル値(論理ピクセル値)が、レンダリング時にデバイスが使用する実際のピクセル数に変換される比率を把握できるようになりました。devicePixelRatio が 2 の iPhone 4S の場合、100 px の論理値は 200 px のデバイス値に相当します。

興味深い機能ですが、デベロッパーにとってどのようなメリットがありますか?初期の頃、これらのデバイスによって画像がアップスケールされていることに気づきました。要素の論理ピクセル幅で画像を作成していたため、画像が描画されると devicePixelRatio でアップスケールされ、ぼやけていました。

devicePixelRatio が原因で拡大され、ぼやけた画像
図 1 - devicePixelRatio による拡大とぼかしによる画像

デフォルトの解決策は、devicePixelRatio で拡大した画像を作成し、CSS を使用して同じ量だけ縮小することでした。キャンバスでも同じことが言えます。

function setupCanvas(canvas) {
  // Get the device pixel ratio, falling back to 1.
  var dpr = window.devicePixelRatio || 1;
  // Get the size of the canvas in CSS pixels.
  var rect = canvas.getBoundingClientRect();
  // Give the canvas pixel dimensions of their CSS
  // size * the device pixel ratio.
  canvas.width = rect.width * dpr;
  canvas.height = rect.height * dpr;
  var ctx = canvas.getContext('2d');
  // Scale all drawing operations by the dpr, so you
  // don't have to worry about the difference.
  ctx.scale(dpr, dpr);
  return ctx;
}

// Now this line will be the same size on the page
// but will look sharper on high-DPI devices!
var ctx = setupCanvas(document.querySelector('.my-canvas'));
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.lineTo(200, 200);
ctx.stroke();