Canvas - 02

캔버스(canvas)를 이용한 도형 그리기

그리드


위 그림을 보면 캔버스와 기본 그리드가 놓인 것을 볼 수 있다.
기본적으로 그리드의 1단위는 캔버스의 1픽셀과 같다.
이 그리드의 원점은 좌측 상단의 (0,0)이다.
모든 요소들은 이 원점을 기준으로 위치한다.
그렇기 때문에 파란 사각형의 좌측상단은 왼쪽에서 x픽셀, 위에서 y픽셀만큼 떨어진 것이라 볼 수 있고, 이 사각형의 좌표는 x,y가 된다.

직사각형 그리기

  • SVG와는 다르게 <canvas>는 오직 하나의 원시적인 도형만을 제공한다. 바로 직사각형이다. 다른 모든 도형들은 무조건 하나 혹은 하나 이상의 path와 여러 점으로 이어진 선으로 만들어진다.
    복잡한 도형을 그리기 위해서는 path drawing 함수들을 통해 그릴 수 있다.

  • 직사각형을 그리는 세가지 함수

    • fillRect(x, y, width, height)
      • 색칠된 직사각형을 그린다.
    • strokeRect(x, y, width, height)
      • 직사각형 윤관선을 그린다.
    • clearRect(x, y, width, height)
      • 특정 부분을 지우는 직사각형이며, 이 지워진 부분은 완전히 투명해진다.
  • 각각 세 함수는 모두 같은 변수를 가진다. x, y는 캔버스의 좌측상단에서 사각형의 위치를 뜻하며, width와 height는 사각형의 크기를 뜻한다.

직사각형 도형 예제

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}

fillRect()함수는 가로세로 100픽셀 사이즈의 검정 사각형을 그리고 이후 clearRect()함수가 60 x 60 픽셀의 사각형 크기로 도형 중앙을 지우게 된다.
그리고 strokeRect()가 빈 공간안에서 50 x 50픽셀 사이즈의 윤곽선만 있는 사각형을 만든다.
여기서 볼 수 있 듯이 지웠다고 해서 지속되는 것이 아닌 마치 붓과 지우개를 지우고 다시 붓을 쓰듯 canvas를 그리는 순서에 맞게 그려진다.

경로 그리기

경로(path)는 직사각형 이외의 유일한 원시적인 도형이다.
경로는 점들의 집합이며, 선의 한 부분으로 연결되어 여러가지 도형, 곡선을 이루고 두께와 색을 나타내게 된다. 경로나 하위 경로(sub-path)는 닫힐 수 있다.

경로 만들기 순서

  1. 경로를 생성한다.
  2. 그리기 명령어를 사용하여 경로상에 그린다.
  3. 경로가 한번 만들어졌다면, 경로를 렌더링 하기 위해서 윤곽선을 그리거나 도형 내부를 채울 수 있다.

경로를 그리는 함수

  • beginPath()
    • 새로운 경로를 만든다. 경로가 생성되었다면 이후 그리기 명령들은 경로를 구성하고 만드는데 사용된다.
  • Path 메소드
    • 물체를 구성할 때 필요한 여러 경로를 설정하는데 사용하는 함수이다.
  • closePath()
    • 현재 하위 경로의 시작 부분과 연결된 직선을 추가한다.
  • stroke()
    • 윤곽선을 이용하여 도형을 그린다.
  • fill()
    • 경로의 내부를 채워서 내부가 채워진 도형을 그린다.
  1. 경로를 만들기 위한 첫번째 단계는 beginPath() 메소드를 사용하는 것이다. 내부적으로, 경로는 도형을 이루는 하위경로(선, 아치 등)들의 집합으로 이루어져 있다. 이 메소드가 호출될 때 마다, 하위 경로의 모음은 초기화되며, 우리는 새로운 도형을 그릴 수 있게 된다.

참고
현재 열린 path가 비어있는 경우(beginPath() 메소드를 사용한 직 후, 혹은 캔버스를 새로 생성한 직후), 첫 경로 생성 명령은 실제 동작에 상관 없이 moveTo()로 여겨진다.
그러기에 경로를 초기화한 직후에는 항상 명확하게 시작위치를 설정해 두는 것이 좋다.

  1. 두번째 단계는 실제로 경로가 그려지는 위치를 설정하는 메소드를 호출한다.

  2. 세번째 단계는 선택사항으로 closePath() 메소드를 호출하는 것이다. 이 메소드는 현재 점 위치와 시작점 위치를 직선으로 이어서 도형을 닫는다. 이미 도형이 닫혀있거나 한 점만 존재한다면, 이 메소드는 아무것도 하지 않는다.

참고
fill()메소드 호출 시 , 열린 도형은 자동으로 닫히게 되므로 closePath()메소드를 호출하지 않아도 된다. 이것은 stroke()메소드에는 적용되지 않는다.

삼각형 그리기

function draw(){
        let canvas = document.getElementById('canvas');
        if(canvas.getContext){
            let ctx = canvas.getContext('2d');
            console.log(ctx);

            // 붉은색 물감에 펜을 뭍히고 
            ctx.fillStyle = "#f00"
            //선을 그릴 펜을 잡고
            ctx.beginPath();
            //도화지에 점을 찍고
            ctx.moveTo(75,50);
            //선을 100, 75 픽셀로 이동
            ctx.lineTo(100,75);
            //도화지에 안떼고 이어서 선을 100,25로 이동
            ctx.lineTo(100,25);
            //stroke로 보면 펜이 이동한 경로를 볼 수 있다.
            //ctx.stroke();
            //fill를 사용하면 마지막 펜의 위치를 원점으로 이동시켜 내부를 채운다.
            ctx.fill();
        }
    }

펜(pen) 이동하기

가장 유용한 함수 중에 실제로 어떤 것도 그리지 않지만 위에서 언급한 경로의 일부가 되는 moveTo() 함수가 있다. 이는 펜이나 종이 위에서 들어 옆으로 옮기는 것이라 볼 수 있다.

  • moveTo(x,y)
    • 펜은 x와 y로 지정된 좌표로 옮긴다.

캔버스가 초기화 되었거나 beginPath() 메소드가 호출되었을 때 특정 시작점을 설정하기 위해 moveTo() 함수를 사용하는 것이 좋다. 또한 moveTo() 함수는 연결되지 않은 경로를 그리는데에도 사용 할 수 있다.

다음은 스마일을 그리기위해 중간지점 마다 펜을 들어 지점을 찍어서 다음 도형을 만든 예시이다.

function draw(){
        let canvas = document.getElementById('canvas');
        if(canvas.getContext){
            let ctx = canvas.getContext('2d');

            ctx.beginPath();
            // 겉의 원형을 그린다.
            //x,y,반지름, 시작라디안, 종료라디안, 시계방향 
            ctx.arc(75,75, 50, 0, Math.PI * 2, true);
            //펜을 들어 다음 지점을 찍는다.
            ctx.moveTo(110,75);
            ctx.arc(75,75,35,0,Math.PI, false);
            
            //펜을 들어 다음 지점을 찍는다.
            ctx.moveTo(65,65);
            ctx.arc(60,65,5,0,Math.PI * 2, true);
            
            //펜을 들어 다음 지점을 찍는다.
            ctx.moveTo(95,65);
            ctx.arc(90,65,5,0,Math.PI * 2, true);
            
            ctx.stroke();
        }
    }

만약 moveTo를 하지 않으면 연결된 선을 확인 할 수 있다.

직선을 그리기 위해서는 lineTo() 메소드를 사용할 수 있다.

  • lineTo(x,y)
    • 현재의 드로잉 위치에서 x와 y로 지정된 위치까지 선을 그립니다.

이 메소드는 선의 끝점의 좌표가 되는 x와 y의 두개의 인자가 필요하다. 시작점은 이전에 그려진 경로에 의해 결정되고, 이전 경로의 끝점이 다음 그려지는 경로의 시작점이 된다.
또한 시작점은 moveTo()메소드를 통해 병경될 수 있다.

2개의 삼각형 그리기

function draw(){
        let canvas = document.getElementById('canvas');
        if(canvas.getContext){
            let ctx = canvas.getContext('2d');

            ctx.beginPath();
            ctx.moveTo(25,25);
            ctx.lineTo(105,25);
            ctx.lineTo(25,105);
            //fill은 자동으로 닫아주기 때문에 아래는 필요없다.
            //ctx.closePath();
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(125,125);
            ctx.lineTo(125,45);
            ctx.lineTo(45,125);
            //열린 공간을 닫아준다.
            ctx.closePath();
            ctx.stroke();
        }
    }

호나 원을 그리기위해서는 arc() 혹은 arcTo()메소드를 사용한다.

  • arc(x, y, radius, startAngle, endAngle, anticlockwise)
    • (x, y) 위치에 원점을 두면서, 반지름 r을 가지고, startAngle 에서 시작하여 endAngle 에서 끝나며 주어진 anticlockwise 방향으로 향하는 (기본값은 시계방향 회전) 호를 그리게 된다.
    • x 와 y는 호를 그릴 때 필요한 원점 좌표입니다. 반지름(radius) 은 말 그대로 호의 반지름을 뜻하고 startAngle 및 endAngle 매개 변수는 원의 커브를 따라 호의 시작점과 끝점을 라디안 단위로 정의한다. 이 변수들은 x축을 기준으로 계산된다. Boolean 값을 가지는 anticlockwise 변수는 true일 때 호를 반시계 방향으로 그리게 되며, 그렇지 않을 경우에는 시계 방향으로 그리게 된다.

참고
arc 함수에서 각도는 각이 아닌 라디안 값을 사용한다.
따라서 각도를 라디안으로 바꾸려면 다음의 코드를 사용할 수 있다.

radians = (Math.PI/180)*degrees
  • arcTo(x1, y1, x2, y2, radius)
    • 주어진 제어점들과 반지름으로 호를 그리고, 이전 점과 직선으로 연결합니다.

다양한 호 그리기

function draw(){
        let canvas = document.getElementById('canvas');
        if(canvas.getContext){
            let ctx = canvas.getContext('2d');
            for (let i = 0; i < 4; i++) {
                for (let j = 0; j < 3; j++) {
                    ctx.beginPath();
                    let x = 50 * j + 25; //j가 증가할 수 록 x가 50만큼 이동
                    let y = 50 * i + 25; // i가 증가할 수록 y가 50만큼 이동
                    let radius = 20; // 반지름은 20로 고정
                    let startAngle = 0; // 시작점도 0으로 고정
                    let endAngle = Math.PI + (Math.PI * j) / 2; 
                    //i가 2로 나누어질때 나머지가 0이되면 false, 아니면 true;
                    let anticlockwise = i % 2 == 0 ? false : true; 

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

                    if(i>1){
                        ctx.fill(); // i가 2과 3일때 
                    }else{
                        ctx.stroke(); // i가 0과 1일때
                    }
                }
                
            }
        }
    }

좋은 웹페이지 즐겨찾기