위 그림을 보면 캔버스와 기본 그리드가 놓인 것을 볼 수 있다.
기본적으로 그리드의 1단위는 캔버스의 1픽셀과 같다.
이 그리드의 원점은 좌측 상단의 (0,0)이다.
모든 요소들은 이 원점을 기준으로 위치한다.
그렇기 때문에 파란 사각형의 좌측상단은 왼쪽에서 x픽셀, 위에서 y픽셀만큼 떨어진 것이라 볼 수 있고, 이 사각형의 좌표는 x,y가 된다.
SVG와는 다르게 <canvas>는 오직 하나의 원시적인 도형만을 제공한다. 바로 직사각형이다. 다른 모든 도형들은 무조건 하나 혹은 하나 이상의 path와 여러 점으로 이어진 선으로 만들어진다.
복잡한 도형을 그리기 위해서는 path drawing 함수들을 통해 그릴 수 있다.
직사각형을 그리는 세가지 함수
각각 세 함수는 모두 같은 변수를 가진다. 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)는 닫힐 수 있다.
참고
현재 열린 path가 비어있는 경우(beginPath() 메소드를 사용한 직 후, 혹은 캔버스를 새로 생성한 직후), 첫 경로 생성 명령은 실제 동작에 상관 없이 moveTo()로 여겨진다.
그러기에 경로를 초기화한 직후에는 항상 명확하게 시작위치를 설정해 두는 것이 좋다.
두번째 단계는 실제로 경로가 그려지는 위치를 설정하는 메소드를 호출한다.
세번째 단계는 선택사항으로 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();
}
}
가장 유용한 함수 중에 실제로 어떤 것도 그리지 않지만 위에서 언급한 경로의 일부가 되는 moveTo() 함수가 있다. 이는 펜이나 종이 위에서 들어 옆으로 옮기는 것이라 볼 수 있다.
캔버스가 초기화 되었거나 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() 메소드를 사용할 수 있다.
이 메소드는 선의 끝점의 좌표가 되는 x와 y의 두개의 인자가 필요하다. 시작점은 이전에 그려진 경로에 의해 결정되고, 이전 경로의 끝점이 다음 그려지는 경로의 시작점이 된다.
또한 시작점은 moveTo()메소드를 통해 병경될 수 있다.
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 함수에서 각도는 각이 아닌 라디안 값을 사용한다.
따라서 각도를 라디안으로 바꾸려면 다음의 코드를 사용할 수 있다.radians = (Math.PI/180)*degrees
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일때
}
}
}
}
}