픽셀 밀도(Pixel Density)와 캔버스 애니메이션

pixel density, ppi, dpi

이번 글에서 다룰 내용

  • 픽셀 밀도 Pixel Density

    • 픽셀 밀도란?
    • 픽셀 밀도의 단위 PPI/DPI
    • 픽셀 밀도와 화질
  • 픽셀 밀도를 고려한 캔버스 애니메이션

    • devicePixelRatio
    • 화질 비교
    • 픽셀 밀도 고려해 코드 구현하기

참고

Pixel Density Medium Post
픽셀 밀도 한글 포스트

픽셀 밀도 Pixel Density

픽셀 밀도란?

  • 픽셀 밀도(pixel density)물리적인 1픽셀에 얼마나 많은 픽셀이 들어가는지를 나타내는 것으로 첫 맥에서는 1인치에 72픽셀을 담았다고 한다

    • 현재 100 ~ 150ppi(pixel per inch)사이면 좋은 모니터라고 하며 보통 115 ~ 160ppi 사이라고 한다
    • 2010년 애플이 인치당 두 배의 픽셀을 출력하는 레티나 디스플레이를 선보였고 이후 3x(높이 너비 각각 3배), 4x 등 픽셀 밀도가 높은 화면들이 많이 출시되었다

픽셀 밀도의 단위 PPI/DPI

  • ppi(pixel per inch)디스플레이 장치에서 1인치가 몇 개의 픽셀로 이루어졌는지를 나타낸다
  • DPI(dots per inch)인쇄물에서 1인치가 몇 개의 점으로 이루어졌는지를 나타낸다

    • ppi는 디스플레이에서, DPI는 인쇄물에 쓰이는 것으로 서로 다른 개념이지만 흔히 컴퓨터 스크린을 표현하는데 둘 다 같은 의미로 사용된다 참고
  • 모니터 PPI 사이트에서 제품별 ppi를 확인해볼 수 있고 모니터 DPI 체크 사이트에서는 현재 모니터가 인치당 몇 픽셀을 담고 있는지 알려준다

픽셀 밀도와 화질

  • 픽셀 밀도가 높아지면 아래 그림과 같이 픽셀 하나의 크기는 점점 작아지게 되지만 같은 너비는 더 많은 픽셀들로 이루어지게 된다 image
  • 따라서 아래 그림처럼 ppi가 높아질수록 더욱 정밀한 묘사가 가능해진다 image
  • 하지만 1픽셀짜리 그림을 2x의 고밀도 디스플레이 (레티나 디스플레이)에서 그릴 경우 아래처럼 그림이 작아지게되고, 그림의 크기를 강제로 의도했던 크기로 키울 경우 그림이 깨지게 된다
  • 그림을 보면 장치마다 픽셀 밀도가 달라 이를 고려하지 않은 디자인을 하면 쉽게 화질 저하로 이어질 수 있다 image

픽셀 밀도를 고려한 캔버스 애니메이션

  • 픽셀 밀도를 고려하기 위해서는 일단 픽셀 밀도를 구해야 한다. 이는 devicePixelRatio로 쉽게 구할 수 있다

devicePixelRatio

  • devicePixelRatio는 장치의 픽셀 밀도를 나타내는 값으로 default값인 비율 1은 96DPI 디스플레이를 뜻한다

    • 맥북은 레티나 디스플레이기 때문에 해당 비율이 2가 나온다 (인치당 두 배의 픽셀)
window.devicePixelRatio;

화질 비교

  • 화질 비교는 96dpi 일반 모니터에서 진행했다. 캔버스에 300px*400px 직사각형을 그린 후 윈도우 창을 500% 확대했다. 이렇게 하면 devicePixelRatio는 5가 나오며 이는 맥북 프로보다도 높은 픽셀 밀도를 갖게된다
  • 같은 그림이지만 첫 이미지는 픽셀 밀도를 고려하지 않은 것이라 가장자리가 깨져보이고 두 번째처럼 픽셀 밀도를 고려해 그리면 선명하게 출력된다. (캡처 사진이라 차이가 명확하지 않지만 실제로 확인해보면 차이가 확연하다) image image

픽셀 밀도 고려해 코드 구현하기

  • 픽셀 밀도를 고려한 캔버스 애니메이션을 하기 위해서는 두 가지를 해야 한다

      1. devicePixelRatio만큼 캔버스 키우기
      1. 뷰포트에 맞게 캔버스의 CSS너비 축소하기

1. devicePixelRatio만큼 캔버스 키우기

  • 먼저 원하는 캔버스 사이즈로 stageWidth, stageHeight를 설정한다. default 값인 300px * 150px로 놔둬도 되고 특정 픽셀로 해도 지정해도 된다
  • 윈도우 창에 꽉 차도록 innerWidth와 innerHeight 값으로 설정해봤다
let stageWidth = window.innerWidth;
let stageHeight = window.innerHeight;
  • 그 다음에는 장치의 pixelRatio를 구해서 그만큼 캔버스의 크기를 키운다
  • 캔버스의 width와 height뿐만 아니라 컨텍스트도 동일한 비율로 키워야 한다
let ratio = window.devicePixelRatio;

canvas.width = stageWidth * ratio;
canvas.height = stageHeight * ratio;

ctx.scale(ratio, ratio);

2. 뷰포트에 맞게 캔버스의 CSS너비 축소하기

  • 위 단계까지 진행했으면 캔버스가 의도했던 크기로 나타나지 않고 캔버스가 윈도우 창에 다 담기지 못 해 스크롤을 해야하는 상황이 발생할 수도 있다. 이는 보여지는 css 크기도 달라졌기 때문으로 css를 따로 설정해줘야 한다
canvas.style.width = stageWidth + 'px';
canvas.style.height = stageHeight + 'px';
  • 이제 캔버스는 stageWdith * devicePixelRatio, stageHeight * devicePixelRatio의 가로세로 길이를 갖게 되며 브라우저에서는 stageWidth, stageHeight의 가로세로로 보여지게 된다 (큰 그림을 축소한 것이라고 생각하면 된다)