[HTML/canvas] 기본 사용법과 도형 그리기

mdn의 캔버스 튜토리얼 따라하기
참고 : 캔버스 튜토리얼



<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Canvas Example</title>
    <style type="text/css">
        canvas {
          border: 1px solid black;
        }
      </style>
</head>
<body onload=";">
    <canvas id="tutorial" width="200" height="200"></canvas>
    <button id="btBasicDraw">기본 그리기</button>
    <canvas id="pacmanCanvas" width="200" height="200"></canvas>
    <button id="btDrawPacman">팩맨 그리기</button>
    <canvas id="path2dCanvas" width="200" height="200"></canvas>
    <button id="btDrawPath2d">Path2D 그리기</button>
    <script src="scripts/myCanvas.js"></script>
</body>
</html>
var btBasicDraw = document.getElementById("btBasicDraw");
btBasicDraw.addEventListener("click", basicDraw);

var btDrawPacman = document.getElementById("btDrawPacman");
btDrawPacman.addEventListener("click", drawPacman);

var btDrawPath2d = document.getElementById("btDrawPath2d");
btDrawPath2d.addEventListener("click", drawPath2D);

function basicDraw() {
    var canvas = document.getElementById("tutorial");

    if(canvas.getContext) {
        var ctx = canvas.getContext("2d");

         /** @type {CanvasRenderingContext2D} */ 
        // 사각형 그리기
        ctx.fillStyle = "rgb(200, 0, 0)";
        ctx.fillRect(10, 10, 50, 50); // 색칠된 사각형
        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
        ctx.strokeRect(75, 50, 40, 30); // 윤곽선
        ctx.fillStyle = "rgba(120,219, 213, 1)";
        ctx.clearRect(30, 30, 50, 50); // 특정 부분을 지우는 사각형

        // 삼각형 그리기
        ctx.beginPath();
        ctx.moveTo(125, 125);
        ctx.lineTo(125, 45);
        ctx.lineTo(45, 125);
        ctx.closePath();
        ctx.stroke();

        ctx.beginPath(); // 새 경로 생성
        ctx.moveTo(75, 50); // 펜 옮기기
        ctx.lineTo(100, 75); // 직선 그리기(선의 끝점)
        ctx.lineTo(100, 25);
        ctx.fill(); // fill() 호출 시 열린 도형은 자동으로 닫히게 되므로 closePath() 호출x

        // 스마일 그리기
        ctx.beginPath();
        // 각도는 각이 아닌 라디안 값을 사용, 각도->라디안 : radians = (Math.PI/180)*degrees
        // Math.PI: 180도
        ctx.arc(75,75,50,0, Math.PI * 2, true); // outer circle
        ctx.moveTo(110, 75);
        ctx.arc(75,75,35,0, Math.PI, false); // mouth(clockwise)
        ctx.moveTo(65, 65);
        ctx.arc(60,65,5,0, Math.PI*2, true); // left eye
        ctx.moveTo(95, 65);
        ctx.arc(90,65,5,0, Math.PI*2, true); // right eye
        ctx.stroke();

        // 원 그리기
        for(var i=0; i < 4; i++) {
            for(var j=0; j < 3; j++) {
                ctx.beginPath();

                // 원의 중심 좌표
                var x = 25+j*50;
                var y = 25+i*50;

                console.log("i : ",i, "j : ", j, 'x : ', x, 'y : ', y);

                var radius = 20; // 반지름
                var startAngle = 0; // 시작 각도
                var endAngle = Math.PI + (Math.PI * j)/2; // 끝 각도(180도에서 시작 90도씩 증가)
                var anticlockwise = i % 2 == 0? false : true; // 방향(true : 반시계, false : 시계)

                ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

                if(i > 1) {
                    ctx.fill();
                } else {
                    ctx.stroke();
                }
            }
        }

        // 이차 베지에 곡선(시작점-제어점-끝점)
        // 말풍선 그리기
        ctx.beginPath();
        ctx.moveTo(75, 25);
        // 제어점x, 제어점y, 끝점x, 끝점y
        ctx.quadraticCurveTo(25, 25, 25, 62.5);
        ctx.quadraticCurveTo(25, 100, 50, 100);
        ctx.quadraticCurveTo(50, 120, 30, 125);
        ctx.quadraticCurveTo(60, 120, 65, 100);
        ctx.quadraticCurveTo(125, 100, 125, 62.5);
        ctx.quadraticCurveTo(125, 25, 75, 25);
        ctx.stroke();

        // 삼차 베지어 곡선(제어점1, 제어점2, 끝점)
        // 하트 그리기
        ctx.beginPath();
        ctx.moveTo(75,40);
        ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
        ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
        ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
        ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
        ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
        ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
        ctx.fill();

        // ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 직사각형 그리기2(좌측상단, 넓이, 높이)
        // 실행 전 moveTo()메소드가 자동으로 호출됨. 펜위치가 자동으로 기본 좌표로 초기화 됨
        ctx.beginPath();
        ctx.rect(25, 25, 50, 50);
        ctx.fill();
    }
}

function drawPacman(){
    var canvas = document.getElementById("pacmanCanvas");
    if(canvas.getContext) {
        var ctx = canvas.getContext("2d");

        // 맵 그리기
        roundedRect(ctx, 12,12,150,150,15);
        roundedRect(ctx, 19,19,150,150,9);
        roundedRect(ctx, 53,53,49,33,10);
        roundedRect(ctx, 53,119,49,16,6);
        roundedRect(ctx, 135,53,49,33,10);
        roundedRect(ctx, 135,119,25,49,10);

        // 팩맨 그리기
        ctx.beginPath();
        ctx.arc(37,37,13,Math.PI / 7, -Math.PI / 7, false);
        ctx.lineTo(31, 37);
        ctx.fill();

        // ...
        for(i=0;i<8;i++) {
            ctx.fillRect(51 + i * 16, 35, 4, 4);
        }

        for(i=0;i<6;i++) {
            ctx.fillRect(115, 51 + i * 16, 4, 4);
        }

        for(i=0;i<8;i++) {
            ctx.fillRect(51 + i * 16, 99, 4, 4);
        }

        // 몬스터 몸체
        ctx.beginPath();
        ctx.moveTo(83, 116);
        ctx.lineTo(83, 102);
        ctx.bezierCurveTo(83,94,89,88,97,88);
        ctx.bezierCurveTo(105,88,111,94,111,102);
        ctx.lineTo(111,116);
        ctx.lineTo(106.333,111.333);
        ctx.lineTo(101.666,116);
        ctx.lineTo(97,111.333);
        ctx.lineTo(92.333,116);
        ctx.lineTo(87.666,111.333);
        ctx.lineTo(83,116);
        ctx.fill();

        // 몬스터 흰자
        ctx.fillStyle = "white";
        ctx.beginPath();
        ctx.moveTo(91, 96);
        ctx.bezierCurveTo(88,96,87,99,87,101);
        ctx.bezierCurveTo(87,103,88,106,91,106);
        ctx.bezierCurveTo(94,106,95,103,95,101);
        ctx.bezierCurveTo(95,99,94,96,91,96);
        ctx.moveTo(103, 96);
        ctx.bezierCurveTo(100,96,99,99,99,101);
        ctx.bezierCurveTo(99,103,100,106,103,106);
        ctx.bezierCurveTo(106,106,107,103,107,101);
        ctx.bezierCurveTo(107,99,106,96,103,96);
        ctx.fill();

        // 몬스터 동공
        ctx.fillStyle = "black";
        ctx.beginPath();
        ctx.arc(101,102,2,0,Math.PI * 2, true);
        ctx.fill();

        ctx.beginPath();
        ctx.arc(89,102,2,0,Math.PI * 2, true);
        ctx.fill();

        ctx.beginPath();
        ctx.clearRect(150,12,35,158);
        ctx.clearRect(12,150,138,20);
    }
}

function roundedRect(ctx, x, y, width, height, radius) {
    ctx.beginPath();
    ctx.moveTo(x, y + radius);
    ctx.lineTo(x, y + height - radius);
    ctx.arcTo(x, y + height, x + radius, y + height, radius);
    ctx.lineTo(x + width - radius, y + height);
    ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
    ctx.lineTo(x + width, y + radius);
    ctx.arcTo(x + width, y, x + width - radius, y, radius);
    ctx.lineTo(x + radius, y);
    ctx.arcTo(x, y, x, y + radius, radius);
    ctx.stroke();
  }

function drawPath2D() {
    var canvas = document.getElementById("path2dCanvas");
    if(canvas.getContext) {
        var ctx = canvas.getContext("2d");

        var rectangle = new Path2D();
        rectangle.rect(10,10,50,50);

        var circle = new Path2D();
        circle.moveTo(125,35);
        circle.arc(100, 35, 25,0,Math.PI * 2);

        ctx.stroke(rectangle);
        ctx.fill(circle);

        //시작점, 수평 오른쪽, 수직하강, (-)왼쪽, 시작점
        var p = new Path2D("M10 10 h 80 v 80 h -80 Z");
        ctx.stroke(p);
    }
}