높은 DPI 캔버스

소개

HiDPI 화면은 모든 것이 더 부드럽고 선명하게 보이게 해줍니다. 하지만 개발자에게 새로운 도전과제를 제시하기도 합니다. 이 도움말에서는 HiDPI 화면의 컨텍스트에서 캔버스에 이미지를 그릴 때의 고유한 문제를 살펴봅니다.

devicePixelRatio 속성

처음부터 시작해 보겠습니다. HiDPI 화면이 등장하기 전에는 픽셀이 픽셀이었고 (확대/축소는 잠시 무시하겠습니다) 그뿐이었습니다. 아무것도 변경할 필요가 없었습니다. 너비를 100픽셀로 설정하면 충분했습니다. 그런 다음 처음 몇 개의 HiDPI 휴대기기가 window 객체에 약간 수수께끼 같은 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();