CS

Canvas

juni_shin 2025. 4. 18. 15:29

Canvas란?

  • Canvas는 HTML5에서 제공하는 2D 그래픽을 그릴 수 있는 요소입니다. JavaScript를 사용하여 실시간으로 그래픽을 렌더링할 수 있으며, 게임, 애니메이션, 데이터 시각화 등에 주로 사용됩니다.

 

Canvas 기본 사용법

1. Canvas 요소 생성

<canvas id="myCanvas" width="800" height="600"></canvas>

2. Canvas 컨텍스트 가져오기

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

3. 기본 도형 그리기

 - 사각형

// 채워진 사각형
ctx.fillStyle = 'red';
ctx.fillRect(x, y, width, height);

// 테두리만 있는 사각형
ctx.strokeStyle = 'blue';
ctx.strokeRect(x, y, width, height);

 

 - 원

ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.fill();  // 채우기
ctx.stroke();  // 테두리

 

 - 선

ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();

 

4. 스타일 설정

 - 색상

ctx.fillStyle = 'red';  // 채우기 색상
ctx.strokeStyle = 'blue';  // 테두리 색상

 

 - 선 스타일

ctx.lineWidth = 2;  // 선 두께
ctx.lineCap = 'round';  // 선 끝 스타일
ctx.lineJoin = 'round';  // 선 연결부 스타일

5. 이미지 그리기

const img = new Image();
img.src = 'image.png';
img.onload = () => {
    ctx.drawImage(img, x, y, width, height);
};

6. 애니메이션 구현

 - 기본 애니메이션 루프

function animate() {
    // 1. 화면 지우기
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // 2. 그리기 작업
    
    // 3. 다음 프레임 요청
    requestAnimationFrame(animate);
}

 

 - 성능 최적화

// 이중 버퍼링
const offscreenCanvas = document.createElement('canvas');
const offscreenCtx = offscreenCanvas.getContext('2d');

// 복잡한 그리기 작업을 오프스크린에서 수행
offscreenCtx.drawImage(...);

// 메인 캔버스에 한 번에 복사
ctx.drawImage(offscreenCanvas, 0, 0);

7. 이벤트 처리

 - 마우스 이벤트

canvas.addEventListener('click', (e) => {
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    // 클릭 위치 처리
});

 

 - 키보드 이벤트

window.addEventListener('keydown', (e) => {
    switch(e.key) {
        case 'ArrowLeft':
            // 왼쪽 이동
            break;
        case 'ArrowRight':
            // 오른쪽 이동
            break;
    }
});

8. 그리기 기능

 - 그라데이션

// 선형 그라데이션
const linearGradient = ctx.createLinearGradient(x1, y1, x2, y2);
linearGradient.addColorStop(0, 'red');
linearGradient.addColorStop(1, 'blue');
ctx.fillStyle = linearGradient;

// 원형 그라데이션
const radialGradient = ctx.createRadialGradient(x1, y1, r1, x2, y2, r2);
radialGradient.addColorStop(0, 'red');
radialGradient.addColorStop(1, 'blue');
ctx.fillStyle = radialGradient;

 

 - 패턴

const img = new Image();
img.src = 'pattern.png';
img.onload = () => {
    const pattern = ctx.createPattern(img, 'repeat');
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
};

 

 - 그림자 효과

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

 

 - 클리핑 영역

ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.clip();  // 이 영역 밖으로는 그려지지 않음

9. 변환 기능

 - 이동

ctx.translate(x, y);  // 그리기 시작점 이동

 

 - 회전

ctx.rotate(angle);  // 현재 위치 기준 회전

 

 - 크기 조절

ctx.scale(scaleX, scaleY);  // 크기 조절

 

 - 변환 행렬

ctx.transform(a, b, c, d, e, f);  // 커스텀 변환 행렬 적용
ctx.setTransform(a, b, c, d, e, f);  // 현재 변환 행렬 초기화 후 적용

10. 텍스트 그리기

 - 기본 텍스트

ctx.font = '30px Arial';
ctx.fillText('Hello World', x, y);
ctx.strokeText('Hello World', x, y);

 

 - 텍스트 정렬

ctx.textAlign = 'center';  // left, right, center, start, end
ctx.textBaseline = 'middle';  // top, hanging, middle, alphabetic, ideographic, bottom

 

 - 텍스트 측정

const metrics = ctx.measureText('Hello World');
console.log(metrics.width);  // 텍스트 너비

11. 합성

 - 전역 합성 (캔버스에 여러 요소를 그릴 때, 요소들이 서로 어떻게 겹치고 상호작용하는지 정의하는 속성)

// 1. 기본 모드 - 새로운 도형이 기존 도형 위에 그려짐
ctx.globalCompositeOperation = 'source-over';

// 2. 새로운 도형과 기존 도형이 겹치는 부분만 보임
ctx.globalCompositeOperation = 'source-in';

// 3. 새로운 도형이 기존 도형과 겹치지 않는 부분만 보임
ctx.globalCompositeOperation = 'source-out';

// 4. 새로운 도형이 기존 도형 아래에 그려짐
ctx.globalCompositeOperation = 'destination-over';

// 5. 기존 도형과 새로운 도형이 겹치는 부분만 보임
ctx.globalCompositeOperation = 'destination-in';

// 6. 기존 도형에서 새로운 도형과 겹치지 않는 부분만 보임
ctx.globalCompositeOperation = 'destination-out';

// 7. 새로운 도형이 기존 도형 위에 그려지고, 겹치는 부분은 기존 도형이 보임
ctx.globalCompositeOperation = 'source-atop';

// 8. 새로운 도형이 기존 도형 아래에 그려지고, 겹치는 부분은 새로운 도형이 보임
ctx.globalCompositeOperation = 'destination-atop';

// 9. 겹치는 부분의 색상값이 더해짐 (밝아짐)
ctx.globalCompositeOperation = 'lighter';

// 10. 새로운 도형만 보이고 기존 도형은 모두 지워짐
ctx.globalCompositeOperation = 'copy';

// 11. 겹치는 부분이 투명해짐
ctx.globalCompositeOperation = 'xor';

// 12. 색상 곱하기 효과
ctx.globalCompositeOperation = 'multiply';

// 13. 색상 스크린 효과
ctx.globalCompositeOperation = 'screen';

// 14. 오버레이 효과
ctx.globalCompositeOperation = 'overlay';

// 15. 어두운 색상만 보임
ctx.globalCompositeOperation = 'darken';

// 16. 밝은 색상만 보임
ctx.globalCompositeOperation = 'lighten';

// 17. 색상 닷지 효과
ctx.globalCompositeOperation = 'color-dodge';

// 18. 색상 번 효과
ctx.globalCompositeOperation = 'color-burn';

// 19. 하드 라이트 효과
ctx.globalCompositeOperation = 'hard-light';

// 20. 소프트 라이트 효과
ctx.globalCompositeOperation = 'soft-light';

// 21. 차이 효과
ctx.globalCompositeOperation = 'difference';

// 22. 배제 효과
ctx.globalCompositeOperation = 'exclusion';

// 23. 색상 효과
ctx.globalCompositeOperation = 'hue';

// 24. 채도 효과
ctx.globalCompositeOperation = 'saturation';

// 25. 색상 혼합 효과
ctx.globalCompositeOperation = 'color';

// 26. 명도 효과
ctx.globalCompositeOperation = 'luminosity';

 

 - 투명도

ctx.globalAlpha = 0.5;  // 0.0 ~ 1.0

12. 이미지 조작

 - 이미지 데이터 가져오기

const imageData = ctx.getImageData(x, y, width, height);

 

- 픽셀 조작

const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
    data[i] = 255 - data[i];     // R
    data[i + 1] = 255 - data[i + 1]; // G
    data[i + 2] = 255 - data[i + 2]; // B
    // data[i + 3]는 알파 채널
}
ctx.putImageData(imageData, x, y);

13. 성능 최적화

 - 애니메이션 최적화 (requestAnimationFrame)

  • 브라우저의 렌더링 주기에 맞춰 최적화된 타이밍에 실행됩니다
  • setInterval 이나 setTimeout 대신 사용하는 것이 권장됩니다.
  • 브라우저가 다음 화면을 그리기 전에 실행할 함수를 예약하는 메서드입니다
function animate() {
    // 1. 화면 지우기
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // 2. 그리기 작업
    // 예: 캐릭터 이동, 물체 그리기 등
    
    // 3. 다음 프레임 요청
    requestAnimationFrame(animate);
}

// 애니메이션 시작
requestAnimationFrame(animate);