Javascript/canvas

canvas 기본 내용 빠르게 훑어보기

킹king 2023. 7. 26. 11:06
반응형

canvas는 그림을 그리는데 사용되는 html 태그임. 단순 이미지가 아닌 그래프, 사진, 애니메이션 등을 구현할 수 있음. 이 포스팅에 기본적인 canvas 구현법을 정리해둠.

 

1. canvas에 width, height 설정하기

html 또는 js 둘중 하나로만 설정해야함.

css로 설정하면 canvas 내에 도형을 그렸을때 렌더링이 이상하게 됨 (css 속성과 실제 width/height는 다른 개념이라서 깨지는것).

<canvas width="500" height="700"></canvas>
const canvas = document.querySelector('canvas');

canvas.width = 500;
canvas.height = 700;

 

 

2. canvas에 background 채우기

background 정도는 css로 해도 문제가 없지만 기본적으로 canvas는 css 사용을 권장하지 않는다고 해서 js로 설정하는게 좋음.

원리는 canvas 크기와 똑같은 도형을 하나 소환해서 깔아놓는 것임.

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

ctx.fillStyle = 'gray'; // 한번 설정한 스타일은
ctx.fillRect(0, 0, canvas.width, canvas.height); // 바로 밑에 있는 도형에 적용됨

여기서 getContext('2d')는 canvas내에 출력할 컨텐츠를 생성하고 다룰 수 있게 해주는 객체라서 무조건 변수로 선언해둬야함(webGL같은건 3d 기반).

 

 

3. canvas에 도형 그리기

canvas는 기본도형인 직사각형을 그릴 수 있음. fillRect 함수를 활용하여 x좌표, y좌표, width, height를 설정해서 네모네모를 그릴 수 있음.

ctx.fillRect(0, 0, 10, 10); // x: 0, y: 0위치에 10x10짜리 네모를 그려줌

참고로 canvas 좌표기준은 왼쪽 위임(위 사진에 0, 0으로 되어있는 부분).

 

만약 ctx.fillRect(10, 20, 30, 40) 이라면

 

x: 10 (오른쪽으로 10)

y: 20 (아래로 20)

 

이 지점에다가 30x40짜리 네모를 그린다는 의미!

 

 

4. 도형 그리는 함수 3가지

fillRect(네모 소환), strokeRect(외각선 네모 소환), clearRect(그만큼 영역 소멸) 이렇게 3가지 함수가 있음.

ctx.fillStyle = 'red';
ctx.fillRect (10, 20, 30, 40);  // (10,20)위치에 30x40짜리 네모를 그림.

ctx.strokeRect(50, 20, 30, 40); // (50,20)위치에 30x40짜리 border가 그려진 네모를 그림.

ctx.clearRect(10, 20, 30, 10); // (10,20)위치에 30x10짜리 네모영역을 지움(투명해짐).

이를 나타내면 다음과 같음(canvas에 회색으로 background 깔아둠).

 

빨간색 네모는 fillRect로 그린 네모, 옆에 투명한 네모는 strokeRect, 빨간색 네모 윗부분을 지운(그림판 지우개로 지웠다고 생각하면 됨)건 clearRect임.

 

 

5. 경로 그리는 함수 몇가지

canvas가 그릴 수 있는 또다른 하나는 바로 경로(선)임. 

ctx.beginPath(); // 지금부터 경로(선)를 그릴 것이라 선언.

ctx.moveTo(75, 50); // 시작위치를 x:75, y:50으로 이동.
ctx.lineTo(100, 75); // 시작위치에서 x:100, y:75 여기로 선을 그음.
ctx.lineTo(100, 25); // 아까 그은데서 x:100, y:25 여기로 선을 또 그음.

ctx.fill(); // 열린 도형 닫힘

lineTo로 두군데에 라인을 그려주면 이런 상태라는 것임.

여기서 fill()을 쓰면 마지막점(초록색)과 시작점(빨간색)을 자동으로 이어주면서 채워진 삼각형이 완성됨.

 

참고로 그렇다면 외곽선으로 만들고 싶다면 fill()대신 이렇게 써주면 된다.

ctx.beginPath();

ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
    
ctx.closePath(); // fill처럼 마지막점과 시작점을 연결해주지만 안에 색을 채우진 않음.
ctx.stroke(); // 외곽선 처리

이렇게 나온다!

 

 

6. 원 그리기

원도 그릴 수 있는데 인자 5개가 기본으로 필요함

 ctx.beginPath();

 ctx.moveTo(75, 50);
 ctx.arc(75, 50, 30, 0, Math.PI * 2, true); // (75, 50)을 기준으로 반지름 30짜리 원을 그림.

 ctx.stroke(); // 시작점까지 연결되는 선이 이미 그어져서 closePath()필요없이 바로 stroke() 진행.

설명에 없는 3~5번째 인자는 각각 시작각도/끝각도/방향(기본 시계방향)임.

 

평범한 원을 그릴꺼면 그냥 각도는 시작 = 0, 끝 = Math.PI * 2로 설정해서 그리면 된다.

 

가운데 짝대기는 뭐냐

가운데 저 반지름 나타내는것같은 선은 뭐지라고 한다면, moveTo() 때문에 그러함. 이부분만 주석처리 해주면 그냥 깔끔한 원이 완성된다.

 

색칠하고 싶다면 stroke()까지 하고 난 다음

ctx.fillStyle = "blue";

ctx.fill();

이렇게 두줄 추가해주면 된다.

 

 

7. 선 스타일

도형에 fill이 있다면 선에는 아래와 같은 스타일 지정이 가능함(자주 쓰는 것만).

ctx.lineWidth = 5; // 라인 두께
ctx.strokeStyle = 'red'; // 라인 색
ctx.lineCap = 'round'; // 라인 끄트머리 모양 round, square, butt 
ctx.setLineDash([4, 10]); // 라인 대시 형태 [길이, 간격]
ctx.stroke();

setLineDash의 첫번째 인자가 길이인데, 하나당 길이를 4로 준거임. 1로 변경한다면 쩜쩜쩜쩜 모양으로 바뀌게 된다.

 

 

8. 글자 쓰기 및 스타일

ctx.font = '48px serif';
ctx.fillText('hello', 100, 50); // 뒤에는 x,y 좌표
ctx.strokeText('world', 220, 50); // 뒤에는 x,y 좌표

 

 

9. 그림 불러오기

const img = new Image();
img.src = "./src/images/r.png";

img.addEventListener("load", () => {
  ctx.drawImage(img, 0, 0, 100, 100); // x, y, width, height
});

참고로 width, height 안쓰면 원래 이미지 크기로 출력된다. 그리고 drawImage()는 인자가 굉장히 많은데, 큰 이미지에서 특정 부분만 캔버스에 나타낼수도 있음.

 

 

10. 그림 그리기

소위 말해 그림판 기능을 위해 마우스 이벤트를 등록시킬때가 있음.

canvas.addEventListener("mousedown", mousedown);
canvas.addEventListener("mousemove", mousemove);
canvas.addEventListener("mouseup", mouseup);
canvas.addEventListener("mouseout", mouseout);

let can = {
  drag: false,
  sx: null, // startX라는 의미로 시작위치의 x좌표.
  sy: null, // startY라는 의미로 시작위치의 y좌표.
};

function mousedown(event) {
  // 마우스 눌렀을 때 그리기 시작한다는 것을 알려줌.
  can.drag = true;

  // 이때 시작 위치를 기록하기 위해 저장함(중요).
  setStartPosition(event);
  
  // 드래그 안하고 클릭만 해도 점이라도 그려지게 하기 위해서 추가.
  draw(event);
}

function mousemove(event) {
  // 마우스 눌러서 그리기 시작한다는걸 알려주지 않았을땐 나가기.
  if (!can.drag) return;

  // 마우스 따라서 그림.
  draw(event);

  // 현재 위치를 시작위치로 덮어씌움(중요).
  setStartPosition(event);
}

function mouseup(event) {
  // 마우스 때면 끝.
  can.drag = false;
}

function mouseout() {
  // 마우스 때면 끝.
  can.drag = false;
}

function draw(event) {
  ctx.beginPath();

  // 처음위치부터 시작해서
  ctx.moveTo(can.sx, can.sy);
  // 현재 위치까지 그림.
  ctx.lineTo(event.offsetX, event.offsetY);

  ctx.stroke();
}

function setStartPosition(event) {
  can.sx = event.offsetX;
  can.sy = event.offsetY;
}

제일 중요한건 위치를 기록해야한다는 것임 (위에 짤은 위치 기록을 안하고 ㄱ을 마우스로 쓴 상황)

 

그리고 그림판 기능을 만들꺼라면 lineWidth를 round로 미리 설정해줘야함. 왜냐면 크기를 키우고 썼을때 round적용을 안했다면 아래와 같은 사단이 나기 때문!!

 

 

11. 그린 그림 다운로드 하기

아래 예시는 버튼이 a태그가 아니라서 가상의 a태그를 하나 만들어서 다운로드 했는데, a태그로 버튼을 만들었다면 이하 부분은 제외해도 됨.

  const img = canvas.toDataURL("image/png");
  
  // 가상 a태그
  const a = document.createElement("a");
  a.href = img;
  a.download = "canvas-image.png";

  document.querySelector("body").append(a);
  a.click(); // 자동 활성화 시켜줌

  document.querySelector("body").removeChild(a);

모든 게시물(특히 과거 게시물)은 잘못된 방법으로 처리한것을 좋다고 써놨을 수 있습니다. 참고만 하시고 틀린게 있다면 댓글 남겨주세요~