[JavaScript] 그림판만들기(Camvas이용)


1️⃣ 목표

  • Canvas API를 이용해 그림판을 만들어볼 계획인데 기본 베이스노마드코더 강의를 보고 만들었습니다. (그렇기 때문에 해당내용은 작성하지 않았지만 노마드코더 사이트에서 무료로 배우실 수 있습니다.)
    👉🏻👉🏻👉🏻바닐라 JS로 그림판 만들기 - 노마드코더(니콜라스)

  • 이렇게 만들어진 기본 그림판추가적인 기능을 구현할 예정입니다.
  • 이전에 <p5.js>를 이용하여 “raycasting”을 구현하는 법을 배운적이 있습니다. <p5.js>Canvas API포함하고 있는 라이브러리입니다. 여기서 Canvas의 강력한 기능을 경험한적이 있습니다.
  • 이처럼 Canvas API관련 라이브러리가 많기 때문에 강력하지만 다소 어려울 수 있는 Canvas사용을 쉽게 다룰 수 있게 해줍니다.
  • 하지만 이번에는 라이브러리 사용없이 기본적인 Canvas를 이용할 것입니다.

🎨 Canvas API 설명사이트

👉🏻 Canvas API - MDN

🎨 p5.js 공식사이트

👉🏻 https://p5js.org/ko

🍌 p5.js를 이용하여 raycasting을 구현한 내용의 포스트

👉🏻 자바스크립트로 raycasting 구현하기

2️⃣ 도구선택 유연하게 만들기

(1) 기존의 도구선택방식

  • 기본적으로 구현한 그림판의 경우 도구가 채우기 두가지 였습니다. let filling = false;
  • 도구두가지이기 때문에 filling변수가 false이면 으로 그리기, true이면 채우기동작을 하도록 구현해도 문제가 없었습니다.

(2) 도구를 세가지이상 늘어난다면?

  • 만약 도구가 세가지 이상으로 늘어난다면, filling과 같이 boolean형으로 사용할 변수가 한개로는 부족해 집니다.
  • 만약 원을 그리는 도구를 추가한다고 하면 다음과 같이 독립적으로 참, 거짓을 판별하는 변수들을 선언할 수 있습니다. let isPaint = false;
    let isPen = false;
    let isCircle = false;
  • 하지만 화면에 그리기위해서는 모두 마우스를 클릭하는 동작이벤트가 겹치기 때문에 사용하는 동구가 아니라면 모두false로 바꿔주어야합니다.
  • 이런식으로 처리하려면 따로 처리하는 함수를 구현해야하고 추후에 새로운 도구가 추가되면 수정하는데도 불편할 것같습니다. (유연하지 못한 코드)

(3) 최종 도구 선택방식

  • 위에서 처럼 boolean형으로 각각의 도구들을 구분하여 사용하는 것은 여러모로 비효율</b>적인 것 같습니다.
  • 대신에 다음과 같이 새로운 방식이 좋을 것같습니다. const PEN = 1;
    const PAINT = 2;
    const CIRCLE = 3;

    let tool = PEN;

  • 위와 같이 매크로 형식으로 도구를 지정해놓고 tool이라는 변수에 선택적으로 지정해서 사용하는 방식입니다.
  • 이제 도구를 설정해주는 함수도 따로 구현할 필요가 없어졌습니다.
if (paint) {
  paint.addEventListener("click", () => (tool = PAINT));
}

if (pen) {
  pen.addEventListener("click", () => (tool = PEN));
}
  • 위와같이 각각의 도구선택 이벤트들도 깔끔하게 구현할 수 있으며 새로운 도구가 추가되어도 유연하게 추가하고 제거할 수 있을 것 같습니다.

3️⃣ 도구에 맞는 이벤트 맞춤설정하기

(1) 이벤트 중복문제

  • 이제 tool이라는 변수로 어떤 도구를 쓰는지 쉽게알 수 있습니다.
  • 하지만 각각의 도구들은 모두 마우스관련 이벤트이고 모두 같은 화면(종이)에서 동작합니다. 결국 "이벤트가 겹칠 수 밖에 없습니다.</b>
  • 물론 마우스 이벤트마다 각각의 도구들if, else로 처리하여 동작을 다르게하도록 구현할 수 있습니다. 마우스 이벤트는 종류는 한정적이기 때문에 새로운 도구가 생겨도 수정하는데 큰어려움이 없을 것같습니다.

(2) 유연성? 성능?

  • 새로운 도구들이 추가될때마다 마우스 이벤트함수들을 수정해주면 유연성(재사용)면에서 좋을 것 같습니다.
  • 하지만 도구들의 종류가 많아지게되면 “마우스 이벤트가 일어날때마다” if, else도구들을 판별하는 과정도 부담스럽게 될 것입니다. (단순히 마우스를 움직이기만해도 if, else로 도구를 탐색…)
  • 이렇게 성능적으로 생각한다면 도구선택함과 동시에 그 도구전용 이벤트를 설정하는 것이 더 좋을 것 같습니다.

(3) 이벤트 통제함수 구현

  • 아직 이벤트함수에 관해 잘 모릅니다. (이벤트 위임, 버블 등등 들어봤지만 앞으로 배워야할 부분)
  • 그래도 이벤트 추가(add), 제거(remove)함수에 대해서는 알기 때문에 이것들을 이용하여 다음과 같이 이벤트를 통제하는 함수(master)를 만들었습니다.

event_func

  • master함수가 호출되면 이전 이벤트 삭제를 하고 도구에 맞는 새로운 이벤트를 생성하는 과정이 일어납니다.
  • 추가, 제거함수들에 각각의 도구에 맞는 이벤트 함수들을 적용해야되기 때문에 처음에 함수를 만들고 추가하는 과정이 번거로울 수 있지만 도구 추가,제거가 편해질 것 같습니다.
  • 무엇보다 마우스 이벤트마다 도구를 판별할 필요가 없어집니다.
if (paint) {
  paint.addEventListener("click", () => master(PAINT));
}

if (pen) {
  pen.addEventListener("click", () => master(PEN));
}
  • 위의 코드처럼 도구를 선택하면 master함수가 호출되어 알맞는 이벤트로 적용됩니다.

(4) 보완해야될 점(클래스화)

  • 도구마다 겹치는 기능들이 있을 것같은데 그것에 대한 처리가 아쉬운 것 같습니다.
  • OOP적인 관점으로 접근한다면 각 도구들마다 클레스를 구현하고 겹치는 기능들은 상속을 이용하여 구현하면 좀 더 깔끔하지 않을까 하는 생각이 들었습니다.
  • 자바스크립트에서 클레스(class)를 이용하는 방법도 공부해 봐야할 것 같습니다.
  • 아직 클레스(class)사용이 서툴지만 이와 비슷하게 사용하기 위해서 다음과 같이 배열을 이용하여 정적 클래스처럼 작동하도록 만들어 봤습니다.

func_array

  • 이것이 올바른 사용방법(클린코드)인지는 모르겠지만 자바스크립트에서는 함수를 배열안에 담을 수 있었습니다.

use func_array

  • 또한 이것정적 클래스처럼 사용할 수 있었습니다.
  • 주의할점은 함수const 변수안에 담기므로 호이스팅(hoisting)이 되지않습니다. (hoisting이란 js에서 var변수, 함수선언이 자동으로 위쪽으로 배치되는 것)

하지만 이것은 어디까지나 나만의 방법으로 작성한 코드입니다. 좋은습관을 잡기위해서는 하루빨리 “js 클린코드 작성하는 법”을 공부해야될 것 같습니다.

4️⃣ 새로운 기능 추가

(1) 디테일 색상툴 이용하기

  • htmlinput태그중에 다음과 같이 디테일하게 색상을 설정할 수 있는 기능이 있었습니다.
<input  type="color" />


color input

  • “input”이라는 이벤트를 사용하면 됩니다.
  • 기본 그림판에서는 이미 다음과 같은 색상툴이 있었습니다.

base color_tool

  • 색상툴의 원리는 style.backgroundColor로 접근해 색을 가져오는 것 입니다.
  • 이와 다르게 새롭게 추가색상툴.value로 색을 가져옵니다.

2colorfunc

  • 즉, 두가지 색상툴핸들링 함수색상을 가져오는 부분만 다를뿐 나머지 코드는 중복됩니다.

1colorfunc

  • 위와같이 하나의 함수로 묶어줬습니다.

(2) 현재색상 표시기능

  • 위와같이 두가지 버전 색상툴을 사용하다보니 현재 어떤색을 사용하는지 햇갈렸습니다.

code

  • 위의 (1)에서 만들어준 색상 핸들링 함수(changeColorFunc)에서 색상업데이트해주도록 했습니다.

display_tool

(3) 반응형 종이

  • 기본 그림판캔버스 종이의 크기가 고정된 값입니다. 이것을 반응형 종이로 만들었습니다. (반응형 css요소설정 설명은 생략)
  • canvas API는 다음과 같은 초기 설정이 필요합니다.

display_tool

  • 이렇게 생성된 ctxcanvas노드의 width, height요소에 의존적입니다.
  • 그렇기 때문에 크기값이 변하는 반응형 종이를 사용하기 위해서는 다음과 같이 “resize”이벤트를 이용하여 width, height를 업데이트 시켜줘야 합니다.
window.addEventListener("resize", () => {
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
});
  • 하지만 "resize"이벤트가 일어나면 기존에 그림판 내용들이 지워졌습니다.
  • 그림판 내용이 지워지지 않게 하기위해서는 그림판에 그림을 그릴때마다 데이터를 저장해주고 "resize"이벤트가 일어나면 다시 저장된 데이터를 불러서 그려주는 작업을 하면 될 것 같습니다. 하지만 코드를 어떻게 구현하지는 아직 제 능력 밖인 것같습니다. 그리고 결론적으로 비효율적인 방법인 것 같고 다른 방법을 찾아봐야할 것 같습니다.

(8.27 추가내용)

  • 아무래도 캔버스 종이의 크기는 반응형보다는 고정된 값을 사용하는 것이 좋을 것 같습니다.
  • 그림같은경우 각자만의 수치로 그림을 그릴텐데 반응형으로 수치가 변하는 것은 말이 안됩니다.
  • 대신에 다른 툴들은 반응형으로 크기가 변할 수 있게 놔두었습니다.
  • 만약 디바이스에 종류에 따라 정밀하게 크기를 지정하고 싶으면 css"@media"기능을 이용하여 커스텀해주면 될 것 같습니다.
  • 결론적으로 반응형화면도 무조건적으로 사용하기보다는 상황에 따라 알맞게 사용해야될 것 같습니다.

5️⃣ 최종 결과물





© 2021.02. by kirim

Powered by kkrim