Javascript

🖼 바닐라 JS로 그림 앱 만들기 : canvas, js 기능 구현 강의 기록

HYEMBURGER 2023. 4. 10. 21:11

 

 

 

 

서론

노마드코더의 강의 중 하나인 바닐라 JS로 그림 앱 만들기 를 완강했다.

공부 중에 새롭게 알게된 것, 기록해놔야겠다고 생각했던 것들 ... 을 모두 모아 기록해둔다.

canvas는 정말 재미있는 기능이어서 매우 흥미로웠다! 꼭 활용해보고 싶다.

 

🔗 MDN Canvas Tutorial 

 

 


 

 

 

<canvas>

HTML의 요소.

보통 자바스크립트를 사용하여 그림을 그리는 데에 사용한다.

ex) 그래프 그리기, 사진 합성, 애니메이션 만들기 ... 등 

 

canvas 크기 지정

// canvas
const canvas = document.querySelector("canvas");

// canvas width, height 지정
canvas.width = 가로;
canvas.height = 세로;

 

canvas 렌더링 컨텍스트 지정

// 렌더링 컨텍스트 타입을 지정 (ex. 2d, bitmaprenderer... )
const ctx = canvas.getContext("2d");

+ WebGL (Javascript API) 을 사용하여 3D 그래픽 애플리케이션을 만들 수 있다. 🔗여기

 

fillRect / strokeRect

// fillRect => 함수가 실행되면 fill(채우기)이 실행된다 (시작x, 시작y, 채울w, 채울h) 
// -----> fill, rect를 함께 담은 단축함수shortcut
// strokeRect 는 fill 을 strok(선)으로만 바꾸면 같다. 
// rect 만 있는 상태는 색상자체는 "투명"이라고 생각하자, 꼭 색이나 선을 채워줘야 한다

ctx.fillRect(50, 50, 5, 5);
// 는 밑과 같다.

ctx.rect(50, 50, 5, 5);
ctx.fill();

 

beginPath()

// beginPath()를 사용하면 사용하기 전과 후 경로가 분리된다.
// 새로운 경로가 시작된다는 뜻.
ctx.beginPath();

 

moveTo / lineTo

// moveTo : 브러쉬를 이동한다. (시작지점)
ctx.moveTo(50,50);

// lineTo : 선을 그린다 (설정한 곳으로 경로가 이어지며 점이 이동한다고 생각해보자)
ctx.lineTo(150, 50);

// 지금까지 이어둔 경로를 입맛에 맞춰 fill 혹은 stroke
ctx.fill();
ctx.stroke();

 


 

 

스타일 지정하기

 

1. 색상 지정

// 색상 지정
fillStyle = "color";
strokeStyle = "color";
// color에는 색상 이름, rgb, rgba, #코드 등이 들어갈 수 있음

 

2. 선 두께 설정

// 선 두께 설정
// 설정을 이런식으로 변경하면 그 이후 선은 계속 똑같은 두께가 나오므로 주의한다.
ctx.lineWidth = number;

 

3. 선 모양 설정

// 기본값은 네모 뾰족하게 나온다 ... 
ctx.lineCap = "round" // 끝이 둥글게 표현
// round, butt, square

🔗 그밖의 스타일

 

 


 

 

 

🤓 기능 구현

0. 마우스를 누르고 있는가?

마우스를 꾹~ 누르고 있는지를 판별한다. (true)

마우스를 누르고 있다면 움직이는대로 그릴 것이고, 그렇지 않다면 계속해서 그리기 시작하는 좌표가 달라진다.

let isPainting = false;

function startPainting() {
  isPainting = true;
}

function cancelPainting() {
  isPainting = false;
}

canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", cancelPainting);

mousedown 

포인터가 요소 내부에 있을 때, 포인팅 장치 버튼을 눌렀을 때 mousedown 이벤트가 발생한다.

mouseup 

버튼을 누른 상태에서 요소 내부에 있는 상태에서 해제되었을 때 이벤트가 발생한다.

 

💥 주의

요소 외부에서 mouseup 이 되면 어떻게 될까?

결론부터 말하자면, mouseup 이벤트가 일어나지 않는다. 요소 외부로 나갔기 때문이다.

때문에 요소 외부로 나갔을 때의 이벤트, 함수를 설정해주어야 한다.

// 포인터가 요소를 떠났을 때도 mouseup 때 실행되는 함수를 담는다.
canvas.addEventListener("mouseleave", cancelPainting);

mouseleave 는 말그대로 포인터가 요소의 밖으로 이동했을 때를 뜻한다.

 

1. 브러쉬 이동, 그리기

- 마우스를 누르지 않았을 때 : 브러쉬를 이동시키기만 한다. 

- 마우스를 눌렀을 때 : 누른 좌표에서부터 움직이는 쪽으로 선을 긋는다.

function onMove(event) {
	// 마우스를 누른 상태
	if(isPainting) {
		ctx.lineTo(event.offsetX, event.offsetY);
        ctx.stroke();
        return;
	}
    // 마우스를 눌렀다 뗐을 때 그 다음에 그릴 선의 경로를 새로 만들어 둔다.
    ctx.beginPath();
    
    // 마우스를 누르지 않고 이동만 하는 상태
    ctx.moveTo(event.offsetX, event.offsetY);
}

 

2. 브러쉬 두께 설정

<input type="range" id="line-width" min="1" max="10" value="5" step="0.1" />

range 타입의 input. 최저는 1 최대는 10 기본값은 5. 0.1씩 증가한다. 라는 정보가 담겨있다.

function onLineWidthChange(event) {
  ctx.lineWidth = event.target.value;
}

lineWidth.addEventListener("change", onLineWidthChange);

 

3. 브러쉬 컬러 설정

function onColorClick(event) {
  const colorValue = event.target.dataset.color;
  ctx.strokeStyle = colorValue;
  ctx.fillStyle = colorValue;
  color.value = colorValue;
}

 

4. 채우기

0~1에서 처럼 채우기 버튼을 만들고, 채우기 버튼이 클릭되어 있을 때(true) 캔버스를 클릭하면 캔버스 크기만큼 fillRect 해준다.

 

5. 캔버스 모두 지우기

두 가지 방법이 있다.

첫 번째, 캔버스크기만큼 white로 채우는 방법

두 번째, celarRect()를 사용하는 방법 

 

첫 번째의 경우 4번을 참고해서 만들면 된다.

두 번째의 경우는 기록해둔다.

function allClear() {
  ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
}

 

6. 지우개

5번의 첫 번째 방법에서 사용했던 것처럼 하얀색으로 덧칠해 지운다고 생각한다.

function onEraserClick() {
  ctx.strokeStyle = "white";
}

선의 색상을 하얀색으로 바꿔주기만 하면 된다. 간단!

 

7. 이미지 가져오기

기본적으로 브라우저는 나의 파일들을 자기 맘대로 볼 수 없다.

내가 파일을 업로드해야만 브라우저의 자바스크립트에게 보이게 된다.

function onFileChange(event) {
  const file = event.target.files[0];
  const url = URL.createObjectURL(file); // url 요청
  const image = new Image(); // = <img src = "" />
  image.src = url;
  image.onload = function () {
    ctx.drawImage(image, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
    fileInput.value = null; // 다른 이미지를 추가하고 싶을지도 모르므로!!
  };
}

fileInput.addEventListener("change", onFileChange);

 

8. 텍스트 추가

캔버스를 더블클릭했을 때, 그 좌표에 input에 적었던 텍스트가 찍힌다.

function onDoubleClick(event) {
  const text = textInput.value;
  if (text !== "") {
    ctx.save(); // 현재 상태, 색상, 스타일 등 모든 것 저장
    ctx.lineWidth = 1;
    ctx.font = "48px serif"; // size, font-family 설정 가능
    ctx.fillText(text, event.offsetX, event.offsetY); // 클릭한 좌표
    // fillText, strokText 두 가지 버전이 있다.
    // 선 굵기 이전으로 되돌리기
    ctx.restore(); //* save() ~ restore() 사이에는 어떤 수정이 이루어져도 저장되지 않는다.
  }
}

canvas.addEventListener("dblclick", onDoubleClick);

save() ~ restore() 

현재 상태, 색상, 스타일 등 모든 것을 저장한다.

이 사이에 어떤 수정이 이루어져도 저장되지 않는다. 

 

9. 이미지 저장하기

캔버스를 이미지로 저장한다.

function onSaveClick() {
  const url = canvas.toDataURL(); // 이미지를 텍스트로 인코딩 해준다.
  const a = document.createElement("a");
  a.href = url;
  a.download = "myDrawing.png";
  a.click();
}

saveBtn.addEventListener("click", onSaveClick);