본문 바로가기
코딩이야기/JS-그림판 만들기

[JavaScript] - 그림판 만들기 ① (그리기, 굵기 조절)

by TaeHyeon0412 2022. 7. 23.

그림판을 만들 때에는 canvas라는 api를 이용합니다.

html에 <canvas></canvas> api를 만들어 준 뒤
const ctx = canvas.getContext("2d");  getContext라는 메소드는
canvas에 2d,3d 선택지를 주는데
2d를 선택해줍니다.

그 후 JS와 CSS에 각각 width와 height 값을 줍니다.
그리고 JS에서는 변수로 만들어 줍니다.
ex)

const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 800;
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;

canvas의 값을 css와 JS에 둘 다 주는 이유 : 
css는 canvas의 크기를 지정해주고 JS에는 canvas의 그리기 영역을 지정해주는 것입니다.

canvas 그리기의 기본원리는 moveTo() = 시작 좌표, lineTo() = 끝나는 좌표를 받아와서
stroke()로 선을 그려주는것입니다.

 

canvas에서 사용자 좌표 찾는법 

우선 JS에서 html의 canvas를 변수로 지정합니다.

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

사용자 좌표는 mousemove라는 이벤트를 이용해서 찾을 수 있습니다.

canvas에 mousemove 이벤트를 추가해준뒤 console.log 해주면  
사용자의 마우스 움직임에 따른 좌표가 표시됩니다.

 

clientX,Y는 window기준의 마우스 좌표이고 
offsetX,Y는 canvas위에 움직이는 마우스 좌표입니다.

사용자 좌표를 찾았으면 let으로 업데이트 가능한 변수를 하나 만들어 줍니다.
이 변수는 마우스 클릭시 true,false로 바뀌면서 함수를 제어해주는 용도로 쓰입니다.
let painting = false;           (초기값은 false, true 아무거나 상관없습니다)


사용자 좌표와 mousemove이벤트를 이용해 함수를 만듭니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
 
function mouseMove(event) {
  if (painting) {
    ctx.lineTo(event.offsetX, event.offsetY);
    ctx.stroke();
    return;
  }
  ctx.moveTo(event.offsetX, event.offsetY);
}
 
 
canvas.addEventListener("mousemove", mouseMove);
 
cs

 

if문을 이용해 let변수가 true이면 사용자의 마우스 위치까지 stroke해줍니다.
if문에 걸리지 않으면 moveTo로 사용자의 마우스 위치를 시작지점으로만 잡습니다.

다음은 마우스up이벤트와 마우스down이벤트를 만들어 줍니다. (위에서 만든 if문 기준)
mousedown일때 let변수 = true (클릭시작)
mouseup일때 let변수 = false (클릭끝)

1
2
3
4
5
6
7
8
9
10
function paintingStart() {
  painting = true;
}
 
function paintingEnd() {
    painting = false;
}
 
canvas.addEventListener("mousedown", paintingStart);
canvas.addEventListener("mouseup", paintingEnd);
cs


이제 canvas에 마우스 클릭을 해보면 선이 그려지는걸 볼 수 있습니다.

하지만 mousedown을 유지한 채로 canvas 밖으로 나가면
canvas안에서는 mouseup 이벤트가 발생 하지 않았기 때문에
여전히 mousedown 이벤트가 일어나고 있는 버그가 있는것을 발견할 수 있습니다.


이것을 해결하는 방법은 2가지가 있습니다.

1. 마우스가 canvas를 떠났을때를 감지하는 이벤트를 추가해줍니다.
canvas.addEventListener("mouseleave", 함수명);
이방법을 하면 mouseup이벤트와 mouseleave이벤트는 같은 함수를 씁니다.
(마우스가 canvas를 떠나면 mouseup이벤트를 하는것)

ex)canvas.addEventListener("mouseleave", cancelPainting);
    canvas.addEventListener("mouseup", cancelPainting);

2. document 자체에 mouseup 이벤트를 추가해줍니다.
ex)document.addEventListener("mouseup",함수명);

 

다음은 선의 굵기를 조절해주는 input을 만들어 줍니다.

html에 input추가 type는 range로 설정.
<input class="line-width" type="range" min="1" max="10" value="5" step="0.01" />
최소값 최대값을 설정합니다. 기본은 최소값부터 1씩 증가하는데
step으로 소수점단위까지 자유롭게 설정할 수 있습니다. 
value로 초기값을 설정해줍니다.

JS에서 요소를 불러오고
const lineWidth = document.querySelector(".line-width");
ctx.lineWidth = lineWidth.value; 초기값을 설정하기 위해 value값을 넣어줍니다.

input.value를 감지할수 있는 이벤트를 만듭니다. (change 이벤트 이용)

lineWidth.addEventListener("change", lineWidthChange);

input.value 값을 얻으려면 target을 이용합니다.

1
2
3
function lineWidthChange(event) {
  console.log(event.target.value); 
}
cs

 

console.log를 해보면 value의 값이 잘 나오는것을 확인 할 수 있습니다.
input.value값을 lineWidth에 넣어줍니다.

1
2
3
function lineWidthChange(event) {
  ctx.lineWidth = event.target.value;
}
cs

 

하지만 여기서 굵기를 바꾸면 모든 선의 굵기가 같이 바뀌는 버그가 생깁니다.

 

이것은 beginPath()를 써서 해결해줄 수 있습니다.
beginPath()는 이전에 했던 것과 별개로 새로운 path를 시작해주는 메소드라서
이벤트가 한번 끝나면 이전과는 연결고리를 끊어버립니다.
하나의 작업(그리기)이 끝날때, 시작할때 아무곳에나 추가하면됩니다.

 


그리기, 굵기 조절 완성!

 

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const lineWidth = document.querySelector(".line-width");
 
const CANVAS_WIDTH = 400;
const CANVAS_HEIGHT = 400;
 
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
 
ctx.lineWidth = lineWidth.value;
 
let painting = false;
let filling = false;
 
function paintingStart() {
  painting = true;
}
 
function paintingEnd() {
  painting = false;
  ctx.beginPath();
}
 
function mouseMove(event) {
  if (painting) {
    ctx.lineTo(event.offsetX, event.offsetY);
    ctx.stroke();
    return;
  }
  ctx.moveTo(event.offsetX, event.offsetY);
}
 
function lineWidthChange(event) {
  ctx.lineWidth = event.target.value;
}
 
canvas.addEventListener("mousedown", paintingStart);
canvas.addEventListener("mouseup", paintingEnd);
canvas.addEventListener("mousemove", mouseMove);
canvas.addEventListener("mouseleave", paintingEnd);
lineWidth.addEventListener("change", lineWidthChange);
cs