높은 DPI 캔버스

폴 루이스

소개

HiDPI 화면은 사랑스럽습니다. 모든 것이 더 부드럽고 깔끔하게 보입니다. 하지만 개발자에게는 새로운 과제가 되기도 합니다. 이 도움말에서는 HiDPI 화면의 컨텍스트에서 캔버스에 이미지를 그릴 때 발생하는 고유한 문제를 살펴보겠습니다.

devicePixelRatio 속성

처음부터 시작해 봅시다. HiDPI 화면이 생기기 전에는 픽셀이 픽셀이었는데 (확대/축소와 배율을 약간 무시하는 경우), 그게 바로 아무것도 변경할 필요가 없었습니다. 너비를 100픽셀로 설정하는 것만으로 충분합니다. 그런 다음 처음 몇 개의 HiDPI 모바일 핸드셋이 창 객체에 약간 수수께끼 같은 devicePixelRatio 속성과 함께 나타나기 시작했으며, 이 속성은 미디어 쿼리에서 사용할 수 있습니다. 이 속성을 통해 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();