이번 포스팅에서는 본격적으로 app.js파일을 건드려보도록 하겠습니다!

강의 내용 중 #2.0~#2.2 부분을 다루고 있습니다 :)

 

목차


    0. 캔버스 내 마우스 컨트롤

    마우스 컨트롤에 있어서 가장 필요한 부분이 무엇인지에 대해서 생각합니다.

    먼저 캔버스 위에 마우스가 어느 위치에 있는지를 알아야 합니다.

    또한 캔버스 위에서 클릭을 했을 때 색칠이 되고, 클릭을 떼면 더 이상 색칠이 되지 않아야 합니다.

    그래서 다음과 같은 onMouseMove, onMouseDown, onMouseUp, stopPainting 함수를 만들어줍니다.

     

    마우스를 캔버스 위에 올렸을 때 console.log(event)
    클릭했을 때 console.log(event)

     

    위처럼 코드를 짜면서 console.log(event)를 통해 원하는 값을 가져오고 있는지 확인할 수 있습니다.

     

    // app.js
    const canvas = document.getElementById("jsCanvas");
    
    let painting = false; // 클릭중인지 여부를 나타내는 변수
    
    function stopPainting(){
        painting = false;
    }
    
    function onMouseMove(event){ // 마우스가 캔버스 위에 있는지를 나타내는 함수
        // 여기서 관심있는 부분은 캔버스 내 위치인 offset에 대한 부분!
        // client X Y는 윈도우 전체에서 마우스의 위치
        const x = event.offsetX;
        const y = event.offsetY;
    }
    
    function onMouseDown(event){ // 캔버스 안을 클릭했을 때를 나타내는 함수
        painting = true; // 누르면 true
    }
    
    function onMouseUp(event)
        stopPainting(); // 떼면 false - 다른 문장이 필요하므로 stopPainting()을 가져와서 여기서 사용한다!
    }
    
    /*
    이 부분은 stopPainting()을 만들면서 만들지 않을 수 있다!
    function onMouseLeave(event){
        painting = false;
    }
    */
    
    if(canvas){
        canvas.addEventListener("mousemove", onMouseMove); // 캔버스 위 움직임 감지
        canvas.addEventListener("mousedown", onMouseDown); // 클릭했을 때 감지
        canvas.addEventListener("mouseup", onMouseUp); // 클릭을 멈췄을 때 감지
        canvas.addEventListener("mouseleave", stopPainting); // 클릭하다가 캔버스를 벗어났을 때 감지
    }

     


    1. 2D Context

    이제 우리가 만들어놓은 캔버스 위에 각종 요소들을 그리고 넣어보려고 합니다.

    그렇게 하기 위해서는 특별한 작업이 필요한데, 아래 Canvas 관련 mdn 내용을 참고하시면 이해에 도움이 되실 수 있습니다.

    강의 내용에 나온 요소를 말로 간략하게 말씀드리면, canvas는 html의 한 요소인데, 이 canvas는 픽셀에 접근할 수 있는 context를 가질 수 있습니다. context를 통해 픽셀을 다루게 되는 것이죠.

    context를 가지기 위해서는 context variable을 만들면 됩니다. 아래 mdn에는 2d에 관한 내용이 적혀있고, 3d 등 다양한 요소들이 getContext()의 매개변수로 들어갈 수 있습니다.

    context를 만들면 lineWidth, strokeRect, fillRect, moveTo, lineTo, closePath, stroke등 다양한 일들을 할 수 있습니다.

     

    CanvasRenderingContext2D - Web APIs | MDN

    The CanvasRenderingContext2D interface, part of the Canvas API, provides the 2D rendering context for the drawing surface of a

    element. It is used for drawing shapes, text, images, and other objects.

    developer.mozilla.org

     

    본 내용을 다룬 니꼬쌤의 강의에는 까다로운 부분이 굉장히 많습니다. (그래서 니꼬쌤도 한번 더 설명해 주십니다.)

     

    가장 먼저 왜 width와 height를 따로 canvas에 넣어주어야 하는가에 대한 부분입니다.

    app.js에서 따로 canvas라는 element에 width와 height를 부여한 이유는
    js는 동작할 때 html파일로부터 canvas의 생성 내용을 받아오는데, html에서 만든 canvas가 우리 눈에 보여질 때는 css로부터 크기정보를 받아서 그 내용을 바탕으로 보여집니다.
    즉 js가 html로부터 jsCanvas라는 id값을 바탕으로 canvas라는 element를 생성할 때 css에서 지정한 width와 height는 받아오지 않습니다. 따라서 이걸 받아오기 위해서는 니꼬쌤이 강의에서 하신 것처럼 따로 700을 지정하거나, offsetWidth 혹은 offsetHeight 등으로 매번 캔버스를 측정해서 가져오도록 지정해야 합니다. (저는 offset을 사용했습니다)
    이렇게 따로 지정하는 일이 없도록 하기 위해서는
    <canvas id="jsCanvas" width="700" height="700"></canvas>
    이런 식으로 html 내부에 width와 height를 지정하면 따로 element에 값을 부여하지 않아도 정상적으로 그림이 그려지게 됩니다.

     

    그 다음은 moveTo와 lineTo의 차이입니다. moveTo는 포인터가 떠있는 상태로 위치만 이동하고, lineTo는 실제로 path를 생성합니다. lineTo로 그려진 path는 우리 눈에 보이지는 않지만, stroke 명령을 주는 순간 path가 직선으로 이어지게 되는 것이죠. 그 직선이 모여서 우리 눈에 자연스러운 것처럼 보이는 겁니다. (실상은 크게 확대하면 모두 직선입니다!)

     

    그 외의 내용은 강의를 들으시면서 코드를 짜보시고, 잘 이해가 안되시는 부분은 주석을 참고하시면 되겠습니다!

    // app.js
    const canvas = document.getElementById("jsCanvas"); // id를 가져올때는 getElementById!! Class를 가져올 때는 get
    const ctx = canvas.getContext("2d") // context 불러오기 (mdn 참고)
    
    // canvas는 2개의 사이즈를 가져야한다. 우리가 보고 있는 CSS size와 pixel manipulating size를 알아야한다
    // index에서 생성한 canvas는 css로부터 size 정보를 받아오는데, js는 이것을 알 수 없다.
    // 그러므로 여기서 따로 
    // 아래는 pixel을 관리하기 위해서 알아야하는 size를 canvas에 제공한 것이다.
    canvas.width = canvas.offsetWidth; // 강의에서는 700, 700을 넣었지만 실제로는 이렇게 해야 관리가 편하다!
    canvas.height = canvas.offsetHeight;
    
    ctx.strokeStyle = "black"; // 사용하려는 사람이 검정색으로 시작하도록 설정 - 추후 변경하면 그 색으로 선이 그어질 것!!
    ctx.lineWidth = "2.5"; // 아까 만들었던 range를 이용해 선의 굵기를 결정, 초기값 2.5px!
    
    let painting = false; // 클릭중인지 여부를 나타내는 변수
    
    
    function stopPainting(){
        painting = false;
    }
    
    function startPainting(){
        painting = true;
    }
    
    function onMouseMove(event){ // 마우스가 캔버스 위에 있는지를 나타내는 함수
        // 여기서 관심있는 부분은 캔버스 내 위치인 offset에 대한 부분!
        // client X Y는 윈도우 전체에서 마우스의 위치
        const x = event.offsetX;
        const y = event.offsetY;
        if(!painting){ // painting(클릭) 상태가 아니라면
            ctx.beginPath(); // path를 만든다 (path를 현 위치로 초기화한다)
            ctx.moveTo(x, y); // path를 x,y로 옮긴다 - 클릭하면 그 path의 최종 지점이 x와 y로 남는다 (beginPath가 있기에 지워도 상관이 없다)
        } else { // painting(클릭) 상태라면
            // CanvasRenderingContext2D.lineTo()는 현재 sub-path의 마지막 점을 특정 좌표와 '직선'으로 연결한다
            ctx.lineTo(x, y); // 실시간으로 계속해서 클릭한 상태로 이동한 좌표를 따라가서 path를 만든다
            // 6,6에서 클릭한 상태로 마우스를 8,8로 가져갔다면 path는 (6,6)~(7,7), (7,7)~(8,8) 이런 식으로 만들어진다.
            ctx.stroke(); // 그렇게 생성된 path를 이어서 선을 만든다. 즉 매우 작은 직선들이 우리 눈에는 부드럽게 보이는 것이다!
        }
    }
    
    function onMouseDown(event){ // 캔버스 안을 클릭했을 때를 나타내는 함수
        painting = true; // 누르면 true
    }
    
    /* 우리가 만들 로직은 onMouseDown에 들어가면 되기 때문에 이 부분도 생략 후 stopPainting으로 대체
    function onMouseUp(event)
        stopPainting(); // 떼면 false - 다른 문장이 필요하므로 stopPainting()을 가져와서 여기서 사용한다!
    }
    */
    
    /*
    이 부분은 stopPainting()을 만들면서 만들지 않을 수 있다!
    function onMouseLeave(event){
        painting = false;
    }
    */
    
    
    if(canvas){ // 사실상 init와 같은 역할이다 - eventListener들은 한번 작동하면 계속 작동!
        canvas.addEventListener("mousemove", onMouseMove); // 캔버스 위 움직임 감지
        canvas.addEventListener("mousedown", startPainting); // 클릭했을 때 감지
        canvas.addEventListener("mouseup", stopPainting); // 클릭을 멈췄을 때 감지
        canvas.addEventListener("mouseleave", stopPainting); // 클릭하다가 캔버스를 벗어났을 때 감지
    }

     

    현재까지 진행된 결과물!

     


     

    이번 포스팅의 내용부분이 그림판의 가장 핵심이 되기도 하고, 이해하기 어려운 부분이 많아 여기서 끊고 다음 포스팅에서 색칠하는 부분 및 다른 부분에 대한 설명을 이어가도록 하겠습니다!

    읽어주셔서 감사합니다 :)

    반응형
    • 네이버 블로그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기