이전 포스팅에서 그림판에서 가장 핵심적인 부분이라고 할 수 있었던 클릭한 부분을 칠하는 것을 만들어보았습니다.

그렇다면 이번 포스팅에서는 색깔을 바꾸고 붓 사이즈를 바꾸는 것을 해 보도록 하겠습니다!

이번 포스팅의 내용은 강의 #2.3~#2.4를 다루고 있습니다.

 

목차


    0. 붓 색을 바꿔보자!

    먼저 html에 있는 각 색깔들을 담당하는 div의 class에 jsColor를 추가해줍니다. 간단하쥬?

    <!-- index.html -->
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" href="styles.css" /> <!-- style.css 연결 -->
        <title>PaintJS</title>
    </head>
    
    <body>
        <!-- JS를 편집할 때는 ID를 사용하고, css를 편집할 때는 cls를 사용할 예정  -->
        <canvas id="jsCanvas" class="canvas"></canvas> <!-- clas 아닌 id를 사용해도 같다! -->
        <div class="controls">
            <div class="controls__range"> <!-- range 바 생성 --> 
                <!-- 0.1 간격으로 조정 가능하고, 최대 0.1부터 5까지 조정 가능, 초기값 2.5 -->
                <input type="range" id="jsRang" min="0.1" max="5" value="2.5" step="0.1"/>
            </div>
            <!-- 아래 div 생성 단축 명령어 div.controls__btns>button#jsMode+button#jsSave -->
            <!-- Fill, Save 버튼 생성 (Fill 클릭하면 Painting, 다시 클릭하면 Fill) -->
            <div class="controls__btns">
                <button id="jsMode">Fill</button>
                <button id="jsSave">Save</button>
            </div>
            <div class="controls__colors" id="jsColors"><!-- div를 한번에 여러개 생성할 때 div.class명*개수 엔터를 누르면 여러개 생성이 가능하다! -->
                <!-- 다중 커서 - 여러 줄 선택하는 방법은 ctrl+alt를 누른 채로 방향키를 눌러 영역을 선택합니다. -->
                <div class="controls__color jsColor" style="background-color:black"></div>
                <div class="controls__color jsColor" style="background-color:white"></div>
                <div class="controls__color jsColor" style="background-color:red"></div>
                <div class="controls__color jsColor" style="background-color:orange"></div>
                <div class="controls__color jsColor" style="background-color:yellow"></div>
                <div class="controls__color jsColor" style="background-color:green"></div>
                <div class="controls__color jsColor" style="background-color:skyblue"></div>
                <div class="controls__color jsColor" style="background-color:blue"></div>
                <div class="controls__color jsColor" style="background-color:violet"></div>
            </div>
        </div>
        <script src="app.js"></script> <!-- app.js 연결 -->
    </body>
    
    </html>

     

    이제 추가한 jsColor Class를 가진 친구들을 getElementByClassName으로 모두 불러서 colors에 저장합니다.

    colors는 Object 형태의 데이터를 가지고 있는데, 이 데이터들을 array 형태로 바꿔서 사용하기 위해 Array.from()을 사용합니다.

    그리고 이 array에서 하나씩 빼오기 위해서 forEach를 사용하고, 하나씩 빼와서 eventListener가 돌아갈 수 있도록 만들었습니다. (화살표 함수의 설명은 생략합니다!)

    이렇게 eventListener가 각각의 색을 가진 div들에 동력을 제공했다면, 이제 클릭을 할 때마다 이전 포스팅에서 설정했던ctx.strokeStyle을 클릭한 요소의 색으로 바꿔주면 됩니다.

    event.target.style.backgroundColor 위치에 내가 클릭한 div의 색깔 정보가 들어있습니다. 이걸로 ctx.strokeStyle을 바꿔주면 끝입니다!!

    // app.js
    const canvas = document.getElementById("jsCanvas"); // id를 가져올때는 getElementById!! Class를 가져올 때는 get
    const ctx = canvas.getContext("2d") // context 불러오기 (mdn 참고)
    const colors = document.getElementsByClassName("jsColor") // jsColors라는 Class를 가진 모든 요소를 가져온다!
    
    // 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를 이어서 선을 만든다. 즉 매우 작은 직선들이 우리 눈에는 부드럽게 보이는 것이다!
        }
    }
    /* 이 부분도 startPainting()으로 대체
    function onMouseDown(event){ // 캔버스 안을 클릭했을 때를 나타내는 함수
        painting = true; // 누르면 true
    }
    */
    
    /* 우리가 만들 로직은 onMouseDown에 들어가면 되기 때문에 이 부분도 생략 후 stopPainting으로 대체
    function onMouseUp(event)
        stopPainting(); // 떼면 false - 다른 문장이 필요하므로 stopPainting()을 가져와서 여기서 사용한다!
    }
    */
    
    /*
    이 부분은 stopPainting()을 만들면서 만들지 않을 수 있다!
    function onMouseLeave(event){
        painting = false;
    }
    */
    
    function handleColorClick(event){ //클릭했을 때 어떤 반응이 나오게 할 것인지 만드는 함수
        // console.log(event.target.style) 찍어보면 우리가 원하는 것은 backgroundColor
        const color = event.target.style.backgroundColor;
        ctx.strokeStyle = color // strokeStyle을 override! 여기서부터는 클릭한 color로 선이 그려진다!
    }
    
    if(canvas){ // 사실상 init와 같은 역할이다 - eventListener들은 한번 작동하면 계속 작동!
        canvas.addEventListener("mousemove", onMouseMove); // 캔버스 위 움직임 감지
        canvas.addEventListener("mousedown", startPainting); // 클릭했을 때 감지
        canvas.addEventListener("mouseup", stopPainting); // 클릭을 멈췄을 때 감지
        canvas.addEventListener("mouseleave", stopPainting); // 클릭하다가 캔버스를 벗어났을 때 감지
    }
    
    // Array.from 메소드는 object로부터 array를 만든다!
    // array를 주면 그 array 안에서 forEach로 color를 가질 수 있다!
    // colors의 각각에다가 이벤트리스너를 실행하도록 한다
    Array.from(colors).forEach(color => color.addEventListener("click", handleColorClick));
    

    현재까지의 결과물!!

     


    1. 붓 굵기를 바꿔보자!

    위와 같은 원리로 붓 굵기 역시 바꿀 수 있습니다.

     

    아까 만들어둔 Range바를 이용합시다. (이번에는 html파일을 따로 바꿀 필요가 없습니다!)

    range를 element로 받아온 후에 eventListener를 추가해줍시다. input 동작이 들어왔을 때 handleRangeChange 함수가 작동하도록 만듭니다. (if문으로 감쌌는데, 굳이 감싸지 않아도 문제 없습니다!)

    그 후 event.target을 console.log로 뒤져보면 value값이 우리가 원하는 값임을 찾을 수 있습니다.

    마지막으로 이 값을 size에 불러와 lineWidth에 overriding합니다.

    사실상 복습이나 다름 없으니 쉽죠?!

    // app.js
    const canvas = document.getElementById("jsCanvas"); // id를 가져올때는 getElementById!! Class를 가져올 때는 get
    const ctx = canvas.getContext("2d") // context 불러오기 (mdn 참고)
    const colors = document.getElementsByClassName("jsColor") // jsColors라는 Class를 가진 모든 요소를 가져온다!
    const range = document.getElementById("jsRange");
    
    
    // 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를 이어서 선을 만든다. 즉 매우 작은 직선들이 우리 눈에는 부드럽게 보이는 것이다!
        }
    }
    /* 이 부분도 startPainting()으로 대체
    function onMouseDown(event){ // 캔버스 안을 클릭했을 때를 나타내는 함수
        painting = true; // 누르면 true
    }
    */
    
    /* 우리가 만들 로직은 onMouseDown에 들어가면 되기 때문에 이 부분도 생략 후 stopPainting으로 대체
    function onMouseUp(event)
        stopPainting(); // 떼면 false - 다른 문장이 필요하므로 stopPainting()을 가져와서 여기서 사용한다!
    }
    */
    
    /*
    이 부분은 stopPainting()을 만들면서 만들지 않을 수 있다!
    function onMouseLeave(event){
        painting = false;
    }
    */
    
    function handleColorClick(event){ //클릭했을 때 어떤 반응이 나오게 할 것인지 만드는 함수
        // console.log(event.target.style) 찍어보면 우리가 원하는 것은 backgroundColor
        const color = event.target.style.backgroundColor;
        ctx.strokeStyle = color // strokeStyle을 override! 여기서부터는 클릭한 color로 선이 그려진다!
    }
    
    function handleRangeChange(event){ // 조정할 때마다 그에 맞춰서 lineWidth를 변경하는 함수
        const size = event.target.value; // 항상 어떤 값인지 console.log()를 찍어보고 찾아보자!
        ctx.lineWidth = size;
    }
    
    
    if(canvas){ // 사실상 init와 같은 역할이다 - eventListener들은 한번 작동하면 계속 작동!
        canvas.addEventListener("mousemove", onMouseMove); // 캔버스 위 움직임 감지
        canvas.addEventListener("mousedown", startPainting); // 클릭했을 때 감지
        canvas.addEventListener("mouseup", stopPainting); // 클릭을 멈췄을 때 감지
        canvas.addEventListener("mouseleave", stopPainting); // 클릭하다가 캔버스를 벗어났을 때 감지
    }
    
    // Array.from 메소드는 object로부터 array를 만든다!
    // array를 주면 그 array 안에서 forEach로 color를 가질 수 있다!
    // colors의 각각에다가 이벤트리스너를 실행하도록 한다
    Array.from(colors).forEach(color => color.addEventListener("click", handleColorClick));
    
    if(range){ // getElementById로 잘 받아왔는지 확인
        range.addEventListener("input", handleRangeChange) // input에 반응해야 하기 때문!
    }

    굵기의 차이가 느껴지시나요..?

     


    거의 다 왔습니다! 이제 페인트통 기능과 이미지를 저장하는 기능만 구현하면 끝이네요!

    아마 다음 포스팅 혹은 다다음 포스팅에서 마무리가 될 것 같습니다!

    그렇다면 다음 포스팅에서 뵙겠습니다 :)

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