高 DPI キャンバス

はじめに

HiDPI 画面は魅力的で、すべてが滑らかですっきりとした印象になります。しかし、開発者に新たな課題ももたらします。この記事では、HiDPI 画面のコンテキストでキャンバスに画像を描画する際の独自の課題について説明します。

devicePixelRatio プロパティ

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

それは興味深いことですが、私たちデベロッパーにとってどのような意味があるのでしょうか。私たちは早い時期に、これらのデバイスによって画像が拡大されていることに気づき始めました。要素の論理ピクセル幅で画像を作成していましたが、描画すると 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();