HTML5 Canvas 핵심 프로그래밍 가이드: 그래픽 기초부터 애니메이션까지

Canvas 초기화 및 기본 설정

Canvas 요소를 생성하고 2D 렌더링 컨텍스트를 획득하는 것으로 그래픽 프로그래밍을 시작할 수 있습니다.

const canvas = document.querySelector('#myCanvas');
const ctx = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 600;

선 그리기와 경로 관리

선을 그릴 때는 beginPath()로 새로운 경로를 시작하는 것이 중요합니다. 이 메서드를 사용하지 않으면 스타일이 마지막 설정으로 덮어씌워지는 상태 누적 문제가 발생합니다.

// 올바른 선 그리기 방법
ctx.lineWidth = 8;
ctx.strokeStyle = '#3498db';

ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(250, 150);
ctx.lineTo(50, 250);
ctx.stroke();

ctx.beginPath();
ctx.moveTo(300, 50);
ctx.lineTo(500, 150);
ctx.lineTo(300, 250);
ctx.strokeStyle = '#e74c3c';
ctx.stroke();

경로를 닫으려면 closePath()를 사용합니다. 이 메서드는 현재 지점에서 시작 지점으로 직선을 그어 닫습니다.

사각형 그리기

사각형을 그리는 세 가지 주요 방법이 있습니다.

// 1. rect() 메서드 사용
ctx.rect(100, 100, 150, 100);
ctx.fillStyle = 'rgba(52, 152, 219, 0.7)';
ctx.fill();

// 2. fillRect() 메서드로 직접 채우기
ctx.fillStyle = 'rgba(231, 76, 60, 0.5)';
ctx.fillRect(300, 100, 150, 100);

// 3. strokeRect() 메서드로 테두리만 그리기
ctx.lineWidth = 3;
ctx.strokeStyle = '#2ecc71';
ctx.strokeRect(500, 100, 150, 100);

별 모양 그리기

삼각함수를 활용하여 복잡한 도형을 생성할 수 있습니다. 다음은 별을 그리는 함수입니다.

function createStar(ctx, centerX, centerY, outerRadius, innerRadius, rotation) {
  ctx.beginPath();
  for (let i = 0; i < 5; i++) {
    const outerAngle = (rotation + i * 72 - 90) * Math.PI / 180;
    const innerAngle = (rotation + i * 72 - 36) * Math.PI / 180;
    
    ctx.lineTo(
      centerX + Math.cos(outerAngle) * outerRadius,
      centerY + Math.sin(outerAngle) * outerRadius
    );
    ctx.lineTo(
      centerX + Math.cos(innerAngle) * innerRadius,
      centerY + Math.sin(innerAngle) * innerRadius
    );
  }
  ctx.closePath();
}

// 200개의 랜덤 별 생성
for (let i = 0; i < 200; i++) {
  const radius = Math.random() * 15 + 10;
  const x = Math.random() * canvas.width;
  const y = Math.random() * canvas.height;
  const angle = Math.random() * 360;
  
  createStar(ctx, x, y, radius, radius * 0.4, angle);
  ctx.fillStyle = `hsl(${Math.random() * 60 + 30}, 80%, 60%)`;
  ctx.fill();
}

변환(Transform) 활용

그래픽 변환은 translate(), rotate(), scale()로 제어합니다. save()restore()를 사용하여 상태를 관리하면 예상치 못한 결과를 방지할 수 있습니다.

function drawTransformedStar(ctx, x, y, size, rotation) {
  ctx.save();
  ctx.translate(x, y);
  ctx.rotate(rotation * Math.PI / 180);
  ctx.scale(size, size);
  
  // 단위 크기의 별 경로 정의
  ctx.beginPath();
  for (let i = 0; i < 5; i++) {
    const outerAngle = (i * 72 - 90) * Math.PI / 180;
    const innerAngle = (i * 72 - 36) * Math.PI / 180;
    
    ctx.lineTo(Math.cos(outerAngle), Math.sin(outerAngle));
    ctx.lineTo(Math.cos(innerAngle) * 0.4, Math.sin(innerAngle) * 0.4);
  }
  ctx.closePath();
  
  ctx.fillStyle = '#f1c40f';
  ctx.fill();
  ctx.restore();
}

그라데이션 효과

선형 그라데이션과 원형 그라데이션을 생성할 수 있습니다.

// 선형 그라데이션
const linearGrad = ctx.createLinearGradient(0, 0, 800, 0);
linearGrad.addColorStop(0, '#ff6b6b');
linearGrad.addColorStop(0.5, '#4ecdc4');
linearGrad.addColorStop(1, '#45b7d1');
ctx.fillStyle = linearGrad;
ctx.fillRect(0, 0, 800, 400);

// 원형 그라데이션
const radialGrad = ctx.createRadialGradient(400, 600, 0, 400, 600, 300);
radialGrad.addColorStop(0, '#ffe66d');
radialGrad.addColorStop(1, '#ff6b6b');
ctx.fillStyle = radialGrad;
ctx.fillRect(0, 400, 800, 400);

패턴 채우기

이미지나 다른 Canvas를 반복 패턴으로 사용할 수 있습니다.

// 이미지 패턴
const img = new Image();
img.src = 'assets/texture.png';
img.onload = () => {
  const pattern = ctx.createPattern(img, 'repeat');
  ctx.fillStyle = pattern;
  ctx.fillRect(0, 0, 800, 600);
};

// Canvas 패턴
function createCustomPattern() {
  const patternCanvas = document.createElement('canvas');
  patternCanvas.width = 40;
  patternCanvas.height = 40;
  const pctx = patternCanvas.getContext('2d');
  
  pctx.strokeStyle = '#34495e';
  pctx.lineWidth = 2;
  pctx.strokeRect(5, 5, 30, 30);
  return patternCanvas;
}

const customPattern = ctx.createPattern(createCustomPattern(), 'repeat');
ctx.fillStyle = customPattern;
ctx.fillRect(0, 0, 800, 600);

둥근 사각형 그리기

네 꼭지점에 원호를 그려 둥근 사각형을 만들 수 있습니다.

function drawRoundedRect(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5);
  ctx.arc(x + width - radius, y + radius, radius, Math.PI * 1.5, 0);
  ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI * 0.5);
  ctx.arc(x + radius, y + height - radius, radius, Math.PI * 0.5, Math.PI);
  ctx.closePath();
}

drawRoundedRect(ctx, 100, 100, 200, 150, 20);
ctx.fillStyle = '#9b59b6';
ctx.fill();

텍스트 렌더링

Canvas에 텍스트를 그리고 측정할 수 있습니다.

ctx.font = 'bold 48px "Segoe UI", sans-serif';
ctx.fillStyle = '#2c3e50';
ctx.fillText('Canvas Graphics', 150, 200);

const textWidth = ctx.measureText('Canvas Graphics').width;
ctx.strokeStyle = '#e74c3c';
ctx.strokeRect(150, 160, textWidth, 50);

그림자 효과

그림자 속성을 설정하여 깊이감을 표현할 수 있습니다.

ctx.shadowBlur = 15;
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';

ctx.fillStyle = '#3498db';
ctx.fillRect(200, 200, 200, 100);

투명도와 합성 모드

globalAlpha와 globalCompositeOperation으로 고급 렌더링 효과를 구현할 수 있습니다.

// 100개의 반투명 원 그리기
ctx.globalAlpha = 0.6;
for (let i = 0; i < 100; i++) {
  const x = Math.random() * canvas.width;
  const y = Math.random() * canvas.height;
  const radius = Math.random() * 40 + 10;
  
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, Math.PI * 2);
  ctx.fillStyle = `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`;
  ctx.fill();
}

// 합성 모드 예시
ctx.globalCompositeOperation = 'lighter';
ctx.globalAlpha = 1.0;

클리핑 영역

clip() 메서드로 특정 영역 밖의 그리기를 제한할 수 있습니다.

ctx.beginPath();
ctx.arc(400, 300, 150, 0, Math.PI * 2);
ctx.clip();

// 이후 그리기 작업은 원 영역 내로 제한됨
ctx.fillStyle = '#e74c3c';
ctx.fillRect(0, 0, 800, 600);

Non-zero Winding Rule

경로의 방향성에 따라 채우기 영역을 결정하는 규칙입니다.

ctx.beginPath();
// 시계 반대방향 (카운터 감소)
ctx.arc(400, 300, 200, 0, Math.PI * 2, true);
// 시계 방향 (카운터 증가)
ctx.arc(400, 300, 100, 0, Math.PI * 2);
ctx.closePath();

ctx.fillStyle = '#3498db';
ctx.fill();

마우스 이벤트 처리

isPointInPath()로 경로 내 클릭을 감지할 수 있습니다.

const circles = [];
for (let i = 0; i < 10; i++) {
  circles.push({
    x: Math.random() * canvas.width,
    y: Math.random() * canvas.height,
    radius: Math.random() * 50 + 20,
    color: '#95a5a6'
  });
}

function renderCircles() {
  circles.forEach(circle => {
    ctx.beginPath();
    ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
    ctx.fillStyle = circle.color;
    ctx.fill();
  });
}

canvas.addEventListener('click', (e) => {
  const rect = canvas.getBoundingClientRect();
  const clickX = e.clientX - rect.left;
  const clickY = e.clientY - rect.top;
  
  circles.forEach((circle, index) => {
    ctx.beginPath();
    ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
    if (ctx.isPointInPath(clickX, clickY)) {
      circle.color = '#e74c3c';
    }
  });
  renderCircles();
});

renderCircles();

애니메이션 구현

requestAnimationFrame이나 setInterval로 부드러운 애니메이션을 만들 수 있습니다.

const balls = [];
const ballCount = 100;

class Ball {
  constructor() {
    this.r = Math.random() * 30 + 10;
    this.x = Math.random() * (canvas.width - this.r * 2) + this.r;
    this.y = Math.random() * (canvas.height - this.r * 2) + this.r;
    this.vx = (Math.random() - 0.5) * 8;
    this.vy = (Math.random() - 0.5) * 8;
    this.color = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, ${Math.random()})`;
  }
  
  update() {
    if (this.x + this.r > canvas.width || this.x - this.r < 0) this.vx *= -1;
    if (this.y + this.r > canvas.height || this.y - this.r < 0) this.vy *= -1;
    this.x += this.vx;
    this.y += this.vy;
  }
  
  draw(ctx) {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
    ctx.fillStyle = this.color;
    ctx.fill();
  }
}

for (let i = 0; i < ballCount; i++) {
  balls.push(new Ball());
}

function animate() {
  ctx.globalCompositeOperation = 'source-over';
  ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  
  ctx.globalCompositeOperation = 'lighter';
  balls.forEach(ball => {
    ball.update();
    ball.draw(ctx);
  });
  
  requestAnimationFrame(animate);
}

animate();

라이브러리 활용

복잡한 차트나 그래프가 필요하다면 RGraph 같은 전문 라이브러리를 고려해보세요. RGraph는 Canvas 기반의 인터랙티브 차트 라이브러리로 바 차트, 파이 차트, 레이더 차트, 선 그래프 등 다양한 시각화를 지원합니다.

태그: html5-canvas javascript-graphics 2d-rendering canvas-animation web-graphics-api

9월 10일 16:12에 게시됨