Skip to content

그래픽과 애니메이션 (Canvas와 그 친구들)

🎯 핵심 문제

과거의 웹페이지는 말라비틀어진 텍스트와 이미지만 보여줄 수 있었습니다. 하지만 벽돌 깨기 게임, 화려한 동적 특수효과, 또는 자유롭게 드래그할 수 있는 데이터 리포트를 만들고 싶다면, <div>만으로는 턱없이 부족합니다. 이것이 바로 Canvas(캔버스)가 탄생한 이유입니다.

이 가이드는 첫 번째 선을 그리는 것부터 시작하여, 최종적으로 브라우저에서 60프레임으로 유연하게 실행되는 파티클 엔진을 직접 작성하는 것까지 안내합니다.


1. Canvas란 무엇인가?

초기의 웹페이지가 레고 블록(HTML 태그)으로 조립한 정적 모델이었다면, HTML5의 <canvas> 태그는 거대한 디지털 백지를 던져주고 코드로 제어되는 을 쥐여준 뒤, 나머지는 모두 당신의 자유에 맡기는 것과 같습니다.

이 안의 그림은 어떤 태그 구조도 가지지 않습니다. 붓으로 칠한 결과물은 일단 그려지면 가장 순수한 "픽셀 물감"이 됩니다.

1.1 Canvas vs SVG: 두 가지 다른 사조의 예술가

프론트엔드 그래픽계에서 Canvas에게는 SVG라는 숙적이 있습니다. 이들은 전혀 다른 두 가지 회화 철학을 대표합니다:

  • Canvas(비트맵 판):
    • 원리: 실제로 종이에 색을 칠하는 것처럼, 몇 번의 붓질이 끝나면 한 덩어리의 물감(픽셀)이 됩니다.
    • 장점: 컴퓨터는 화면에 "물감을 뿌리기"만 하면 되므로 성능이 날아갑니다! 수천 개의 반짝이는 파티클을 동시에 그릴 수 있습니다.
    • 단점: 그린 후에는 개별적으로 되돌릴 수 없으며(DOM 노드로 선택 불가), 확대하면 모자이크가 생겨 흐려집니다.
  • SVG(벡터 그래픽 조립):
    • 원리: PPT를 만드는 것과 같습니다. 원을 그리면 독립적인 태그를 가진 "원 개체"가 화면에 생성됩니다.
    • 장점: 100배든 10만 배든 확대해도 항상 매우 선명합니다. 각 도형은 독립적인 DOM 노드이므로 언제든 CSS와 JS로 색상을 변경하거나 클릭 이벤트를 바인딩할 수 있습니다.
    • 단점: 수만 개의 객체가 날아다니게 하려고 하면, 무거운 DOM 트리와 레이아웃 엔진이 브라우저를 직접 얼려버립니다.

🎮 간단 요약: 동적 게임, 멋진 파티클 특수효과에는 Canvas를; 정밀한 Logo, 상호작용이 명확한 작은 차트에는 SVG를 사용하세요.


2. 첫 번째 선: 직관에 반하는 좌표계 이해하기

2.1 이 종이의 위아래가 왜 뒤집혀 있을까요?

붓을 준비할 때, 먼저 Canvas 안의 자의자가 거꾸로 되어 있다는 것을 알아야 합니다. 전통적인 수학 교과서의 좌표계에서는 중심점 영점이 중간에 있고 위로 갈수록 커집니다. 하지만 컴퓨터 화면 표시 분야에서는 거의 모든 기기의 "원점(0, 0)"이 화면의 가장 왼쪽 상단에 있습니다. 오른쪽으로 가면 X축이 커지는 것은 문제없지만, 아래로 가면 Y축이 커집니다.

Canvas 좌표 시스템의 핵심 원칙:

  • 기본 단위: 픽셀 (px), 화면 물리 픽셀과 1:1 대응.
  • X축: 오른쪽이 양의 방향, 0에서 canvas.width까지.
  • Y축: 아래쪽이 양의 방향, 0에서 canvas.height까지.

👇 아래의 작은 원점을 드래그하여 컴퓨터 그래픽스에서의 좌표 원점과 방향을 직관적으로 느껴보세요:

Canvas Width:600px
Canvas Height:400px
Mouse Position:(0, 0)

2.2 마법의 붓에 양념 칠하기

좌표 체계가 있으면 붓을 소환할 수 있습니다(코드에서는 Context, 또는 약자 ctx라고 함). 실제 물감 팔레트를 들고 그림을 그리는 것처럼, Canvas의 API 설계는 물리적 그림 그리기의 세 단계를 완벽하게 따릅니다:

  1. 색 조합(State): fillStyle로 채우기 색상을, strokeStyle로 테두리 색상을 설정.
  2. 형태 구성(Path): 선(lineTo), 원(arc), 또는 사각형(rect) 중 무엇을 그릴지 구상.
  3. 극단적 붓질(Render): 내부 채우기(fill())를 할지 가장자리 그리기(stroke())를 할지 결정.

Canvas는 순수한 비트맵 캔버스이므로 "한 번 그으면 되돌릴 수 없으며", 한 번 그리면 즉시 픽셀로 말라붙어 독립적인 객체로 되돌릴 수 없습니다.

👇 아래 데모에서 다양한 모양과 색상을 선택해 보고, 배경 코드가 어떻게 위의 "3단계"를 실행하는지 확인해 보세요:

🎨Canvas 基础用代码画图(通俗说:编程画板)

3. 넘기기 애니메이션 책: 화면을 극도로 부드럽게 움직이게 하는 방법

Canvas는 한 번 채우면 영구적인 픽셀이 되는데, 그렇다면 다양한 HTML5 브라우저 게임에서 화면 가득 뛰어다니는 캐릭터들은 어떻게 만들어지는 걸까요?

정답은 "당신의 눈을 속이는 것"입니다. 이는 플립 애니메이션 책이나 영화 필름의 원리와 완전히 같습니다.

  1. 칠판 지우기(Clear): clearRect()로 캔버스 전체의 내용을 무자비하게 비웁니다.
  2. 새 위치 계산(Update): 캐릭터의 X좌표를 앞으로 몰래 2픽셀 추가합니다.
  3. 다시 그리기(Render): 캐릭터를 새로운 위치에 다시 그립니다.
  4. 미친 듯한 반복(Loop): 브라우저에 내장된 극도로 정박한 메트로놈 requestAnimationFrame과 결합합니다. 이는 디스플레이의 주사율(보통 초당 60회, 즉 60 FPS)로 이 세 가지 동작을 반복합니다.

사람의 눈에는 자체적으로 "잔상 효과"가 있어, 초당 60회의 [지우기 -> 업데이트 -> 다시 그리기]에서 깜빡이는 칠판이 아니라 오히려 실크처럼 부드러운 애니메이션으로 보입니다.

👇 아래 데모에서 재생 속도를 조정하고, 각 프레임의 변위가 어떻게 유창한 움직임으로 연결되는지 관찰해 보세요:

FPS:0
Frame:0

4. 코끼리 더듬기: Canvas 안에서 클릭 상호작용은 어떻게 하나요?

Canvas 캔버스는 브라우저의 눈에 어떤 구조도 없는 "물감 천"일 뿐입니다. 캔버스 위에 arc()로 괴물을 그렸다고 가정해 봅시다. "괴물 클릭 시 체력 감소"를 구현하고 싶을 때, 전통적인 document.getElementById를 사용하여 이 괴물을 얻는 것은 불가능합니다. 왜냐하면 HTML 구조에는 600픽셀 너비의 딱딱한 <canvas> 태그밖에 없기 때문입니다.

이것이 그래픽 프로그래밍에서 가장 고전적인 문제입니다: 충돌 감지 (Collision Detection)와 이벤트 위임.

브라우저는 마우스가 Canvas의 화면 좌표 (x, y)를 클릭한 것만 알고 있으므로, 당신이 직접 중학교 기하 수학을 통해 역산해야 합니다:

  • 원의 경우: 피타고라스의 정리를 사용하여 마우스 클릭 위치에서 원 중심 위치까지의 거리를 계산하고, 거리가 반지름보다 작으면 "맞았다"고 판단합니다.
  • 사각형의 경우: 클릭한 x가 사각형의 좌우 경계 안에 있는지, 동시에 y가 상하 경계 안에 있는지 판단합니다.

캔버스에 요소가 아무리 많아도 마우스 호버나 클릭 이벤트는 항상 Canvas라는 유일한 컨테이너에 바인딩되며, 이것이 궁극의 "이벤트 위임"입니다.

👇 아래에서 마우스(클릭, 드래그, 호버) 또는 키보드(방향키 이동)를 사용하여, 이러한 "수동으로 거리 계산"하는 하위 수준 상호작용 논리를 체험해 보세요:

Instructions / 操作说明

  • Click Mode:点击画布创建圆形,按住 Shift 可创建不同颜色

Event Log / 事件日志


5. 연산력 해방: 파티클 시스템과 시각 마법

이 단계에서 "좌표계", "애니메이션 루프" 및 "색상과 모양"을 모두 융합하고 그 수량을 수백 수천 개의 작은 조각으로 폭발시키면, 시각을 폭발시키는 궁극의 무기를 습득하게 됩니다: 파티클 시스템(Particle System).

핵심 아이디어는 극도로 단순하면서도 효과적입니다:

  1. 수백 개의 독립적인 "파티클 객체"로 가득 찬 거대한 배열을 만듭니다.
  2. 각 객체는 자체적인 수명 주기(life), 가속도(vx/vy), 중력 감쇠(gravity)를 가집니다.
  3. requestAnimationFrame이 트리거될 때마다 수백 개의 파티클을 순회하며 업데이트한 다음 렌더링하고, 마지막으로 "사망"(수명이 다하거나/화면 밖으로 나간) 파티클을 조용히 정리합니다.

당신의 브라우저는 순식간에 불꽃놀이, 대설과 폭발을 만들어내는 공장이 될 수 있습니다.

👇 다양한 효과를 클릭하고, 중력과 파티클 수를 조정하며, 가장 단순한 물리 수학 공식이 어떻게 복잡한 집단 시각 효과를 만들어내는지 관찰해 보세요:

Active Particles:0
FPS:0

6. FPS 영광 수호: 뜨거운 CPU에 어떻게 대응할 것인가?

수천 개의 객체를 1초에 60번씩 계산하고 다시 그리는 것은 성능을 매우 많이 소모합니다. 무계획적으로 하면 컴퓨터 팬이 곧 날아오를 것입니다.

다음은 진정한 엔진 전문가들이 프레임 속도를 구하기 위해 사용하는 "호체 절기"입니다:

  1. 부분 칠판 지우기(더티 사각형 Dirty Rect): 캐릭터가 넓은 초원에서 달리고 있을 때, 매 프레임마다 대초원 전체를 clearRect하지 마세요! 캐릭터가 지나간 작은 구역만 "작은 지우개"로 지우고 덮어서 다시 그리면 성능이 즉시 기하급수적으로 향상됩니다.

  2. 백그라운드 대체 마법(오프스크린 Canvas): 배경이 별이 가득한 하늘이거나 복잡하고 화려한 산맥이라면, 매번 실시간으로 렌더링하는 것은 어리석은 짓입니다. 우리는 보통 메모리에 보이지 않는 <canvas>를 몰래 만들고, 그곳에 정교하게 한 번만 그립니다. 이후의 매 프레임 새로고침에서는 drawImage()를 통해 이 합성된 "정적 네거티브"를 직접 붙여내기만 하면, 대량의 기본 계산을 생략할 수 있습니다.

  3. 일괄 붓 세척(Batching): 팔레트에서 빨간색에서 파란색으로 바꾸는 것은 하위 수준에서 비용이 많이 듭니다. 캔버스에 빨간 원 1000개와 파란 원 1000개가 교차로 흩어져 있다면, 가장 빠른 방법은 빨간 물감을 먼저 준비하고 모든 빨간 원을 그린 다음 파란 물감으로 바꿔 모든 파란 원을 그리는 것입니다. 이것이 유명한 일괄 렌더링(Batch Rendering) 사상입니다.

👇 객체 수를 3000 이상으로 끌어올리고 웹페이지가 버벅거리는 심연으로 떨어지는 것을 본 후, 오른쪽 하단의 "최적화 기술" 스위치를 하나씩 켜서 확실한 프레임 속도 구조를 직접 목격해 보세요:

FPS:0
Frame Time:0ms
Objects:1000

7. 전문 용어 요약

용어알기 쉬운 설명
CanvasHTML5가 제공하는 2D 캔버스. 그리기가 극도로 빠르지만, 그린 후에는 물감 픽셀이 되어 DOM 작업을 통한 내용 조작이 불가능합니다.
SVG벡터 그래픽. 확대해도 절대 흐려지지 않으며, 각 도형이 독립적인 태그 요소로 CSS 스타일과 상호작용을 쉽게 바인딩할 수 있습니다.
Context (ctx)당신이 신청한 "2D 마법 붓", 색상 조합, 형태 설정 및 다양한 특수 효과 그리기에 사용됩니다.
requestAnimationFrame브라우저에 내장된 신급 메트로놈, 디스플레이의 주사율에 엄격하게 맞춰 콜백을 실행하며, 부드러운 애니메이션 제작의 필수 선택입니다.
FPS (Frame Rate)프레임 속도. 60 FPS는 1초 동안 브라우저가 캔버스를 60번 매끄럽게 지우고 60장의 새 그림을 다시 그렸음을 의미합니다.
더티 사각형 (Dirty Rect)변화가 발생한 작은 영역에서만 정밀하게 지우고 다시 그려 성능을 강력하게 보존합니다.
오프스크린 Canvas메모리에 숨겨진 "그림자 캔버스". 극도로 복잡하지만 움직이지 않는 배경을 미리 그려두고, 나중에는 정적인 텍스처처럼 반복 사용합니다.

간단한 직선 세그먼트에서부터 웅장하고 화려한 파티클 시스템 엔진까지; 마법처럼 보이는 모든 특수효과는 결국 초당 60회의 좌표 계산과 다시 그리기의 순환일 뿐입니다.