[JS] Canvas 태그를 이용하여 그림판 만들기 -1-

HyunJoo·2021년 2월 8일
3

JavaScript

목록 보기
1/2
post-thumbnail

그림판 만들기 -1-

3줄요약

  1. 이벤트 리스너의 mousedown,mouseup 으로 그리기모드를 실행시킨다.
  2. 그리기 모드일 때 마우스 이동경로에 따라 선을 그린다.
  3. canvas 태그를 이용한다.

기본기능

  • 마우스 클릭시 선 긋기
  • 색상선택 기능
  • 전체 페이지 채우기 기능(색상선택은 필수)
  • 브러쉬 사이즈 조절 기능
  • PNG 파일로 저장가능

구성화면

순서도 (순서설명)

  1. 클릭한 상태에서만 그려질 수 있도록 let으로 상태를 등록해준다.
    mousedown : state -> true / mouseup : state -> false

  2. 캔버스 위에서 마우스 클릭상태에서 움직일 때 지정한 색이 마우스 이동경로로 나타나게 한다.

    1. 캔버스에 이벤트리스너를 등록하여 실시간 마우스의 좌표를 Draw 함수에 넘겨준다.
  3. mousedown : state -> true 일 때만 현재 좌표를 받아와 선을 그린다. mouseup : state -> false 일 때는 return

순서도 풀이

순서도가 어렵다면 기능 하나씩 길게 풀어보자.

1. 색상선택 기능

색상 버튼을 누르면 그 색으로 바뀐다. (기본색이 있다는 거네?)
버튼마다 data-set으로 연결해서 줘도 되고, class 이름자체를 색상으로 하는 것도 나쁘지 않겠군.
순서도 : 버튼을 누른다 => 버튼의 data-set을 ㅇㅇ의 색으로 지정한다.

2. 선 긋기 기능 (draw함수)

캔버스(Canvas) 사용방법 - MDN
canvas라는 것을 알아야한다. 구글검색 : javascript 선 긋기
canvas 태그를 이용해서 선 긋기가 가능하군.
MDN에서 도형을 만들고 있다. 그럼 내가 원하는게 아닌데...
예제를 보면 지정한 점과 지정한 점을 선으로 연결된다.
즉, 마우스의 좌표를 구해서 점을 찍는다고 생각하면 어떠할까?

그럼 점 찍는 방법을 봐보자.

Drawing paths 에 설명되어 있겠군.
beginPath() : path를 만든다. 시작할 때 쓰는거군
stroke() : 도형, 아웃라인, 그리다. 도형의 아웃라인을 그린다는 이야기겠군
fill() : 채우는 거겠군.

The first step to create a path is to call the beginPath(). Internally, paths are stored as a list of sub-paths (lines, arcs, etc) which together form a shape. Every time this method is called, the list is reset and we can start drawing new shapes.
경로를 만드는 첫 번째 단계는 시작 경로()를 호출하는 것이다. 내부적으로, 경로는 함께 모양을 형성하는 서브 경로(선, 호 등)의 목록으로 저장된다. 이 방법을 호출할 때마다 목록이 재설정되고 새로운 모양을 그리기 시작할 수 있다.

대충 내용은 beginPath()로 시작해라. 경로를 만들어라.
조금만 더 읽어보자. Moving the pen! 펜처럼 그리기를 원했어.

moveTo(x,y) : 펜을 좌표로 움직인다.
lineTo(x,y) : 그린다 라인을 현재 그리기 위치에서 x와 y로 지정된 위치로 (나의 발번역)

예제 보니까 조금 감이 왔다.

    // Filled triangle
    ctx.beginPath();
    ctx.moveTo(25, 25);  // 25, 25로 이동
    ctx.lineTo(105, 25); // 25, 25부터 105, 25까지 
    ctx.lineTo(25, 105); // 25, 25부터 25, 105까지 
    ctx.fill(); // 채워

    // Stroked triangle
    ctx.beginPath(); 
    ctx.moveTo(125, 125); // (125, 125)로 이동
    ctx.lineTo(125, 45); // (125, 125)부터 (125, 45)까지
    ctx.lineTo(45, 125); // (125, 125)부터 (45, 125)까지
    ctx.closePath(); // 뭐...니? 
// 이거 빼놓고 해보니까 (125,45) 와 (45, 125)가 연결이 안된다.
// 즉, 다시 moveTo(125, 45)와 lineTo(45,125)할 필요없이 이걸로 다 연결이 된다는 이야기
    ctx.stroke() // 그어!

예제에 나온대로 되는군. 그렇다면 우리가 필요한건 선인데 fill은 아니군. 그럼 stroke로 하자.
나중에 fill로 원하는 크기의 사각형 만들수도 있겠네! 오케이!

그럼 그림판에 대입해보자.

내 좌표가 (100,100) 이라면 moveTo(100, 100) , lineTo(100,100)하면 점이 찍히겠군.
그리고 조금 움직여서 100, 101,102.... 쭉 따라 움직이면 선이 되는군.

이런건 순서도에 '시작점을 받는다, 끝점을 받는다' 보다는
"현재 좌표를 받아와 선을 그린다" 라고 표현해도 되겠네.

3. 현재 좌표 받기

이벤트리스너! 많은 이벤트들 중에 mousemove ! 그리고 event를 인자로 갖는 draw 함수를 호출해주면 되겠군

그런데 좌표를 줘야하는데 event 중 뭘 줘야할까?

clientX를 검색해보니 요런게 있군.

그럼 offsetX, offsetY를 주면 되겠고 이벤트리스너를 window보다는 canvas에만 국한되게 하면 되겠군.

시작

마우스 이벤트에 따른 상태 변화

그림이 그려지는 상황을 보자.
canvas 위에 마우스가 클릭되어 있는 상태에서 이동하면 나와야한다.
mousedown (마우스를 누른상태) 에서 mousemove를 하면 그려지는 거다.
하지만 mouseup이 되면 mousemove해도 안 그려진다.
아래와 같이 할 수 있겠군.

let drawing = false; //기본상태는 대기상태

canvas.addEventListener("mousedown", () => (drawing = true)); // 드로잉 끝
canvas.addEventListener("mouseup", () => (drawing = false)); // 드로잉 대기
canvas.addEventListener("mousemove", draw);

Draw 함수

코드를 작성하기 전에 canvas는 getContext()메서드가 필요하다. 2D 그래픽의 경우 2d를 넣어주면 된다.

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

const draw = e => {
  const x = e.offsetX;
  const y = e.offsetY;
  if (!drawing) return;
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x, y);
  ctx.stroke();
};

이슈 #1 : 점이 안찍힘

점이 안찍힌다.. 뭘까? MDN에 나오는대로 했는데... 곰곰히 생각해보니까
색상도 없고 선굵기도 없네? 찾아보자.
오! Line Styles
lineWidth = value : 라인의 너비를 설정한다.
lineCap = type : 라인 끝의 모양을 설정한다.
strokeStyle = color : 윤곽선 색을 설정한다.
나중에 바뀔 변수들이 수두룩하므로 함수 밖으로 빼놓고 바꾸도록 하자.

ctx.lineWidth = 30;
ctx.lineCap = "round";
ctx.strokeStyle = black;

이슈 #2 : 뭔가 안 맞지?

뭔가 간격이 있어.

CSS에서 크기를 px단위로 하고 있었네. MDN문서에서 px자체를 안쓰고 있었다.

그래서 px를 지우니까 내가 설정했던 크기로 나오지 않는다. 작아...

왜지? 다시 자세히 읽어보니까 아하
HTML에 직접 넣어달라는 이야기군.

<canvas id="canvas" width="500" height="500"></canvas>

이렇게 넣어주니까 원하는대로 잘 나왔다.

완성

2편에서는 색상, 브러쉬크기,전체 페이지 채우기 기능에 대해서 해보도록 하자.

profile
Front-End Engineer

0개의 댓글