높은 DPI 캔버스

소개

HiDPI 화면은 모든 것을 더 부드럽고 깨끗하게 보이게 합니다. 하지만 개발자에게는 새로운 과제도 제시합니다. 이 도움말에서는 HiDPI 화면의 컨텍스트에서 캔버스에 이미지를 그리는 것과 관련된 고유한 과제를 살펴보겠습니다.

devicePixelRatio 속성

처음부터 시작해 보겠습니다. HiDPI 화면이 나오기 전에는 픽셀이 픽셀이었고 (확대/축소 및 크기 조정을 잠시 무시하면) 그게 전부였으므로 아무것도 변경할 필요가 없었습니다. 너비를 100px로 설정하면 그게 전부였습니다. 그 후 window 객체에 약간 수수께끼 같은 devicePixelRatio 속성이 있고 미디어 쿼리에서 사용할 수 있는 최초의 HiDPI 휴대전화가 등장하기 시작했습니다. 이 속성을 사용하면 CSS의 픽셀 값 (논리적 픽셀 값이라고 함)이 렌더링 시 기기에서 사용하는 실제 픽셀 수로 변환되는 비율을 파악할 수 있습니다. devicePixelRatio가 2인 iPhone 4S의 경우 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();