그림판 지우개 구현하기

너구리오리·2021년 8월 4일
0
post-thumbnail

그림판에 선을 추가하는 펜이 있다면 선을 지우는 지우개도 있어야겠죠?

이번 포스트에서는 HTML canvas 태그와 JavaScript의 clearRect()함수를 이용해서 지우개를 구현해보겠습니다.

clearRect() 함수

캔버스에서 사용할 수 있는 clearRect(x, y, width, height) 함수는 직사각형 내부의 영역을 지웁니다.
관련 포스트 보기

여기서 x와 y는 직사각형의 왼쪽 위 꼭지점의 좌표, width와 height는 그 점으로부터 수평/수직으로 정해지는 너비와 높이입니다.

clearRect() 함수를 이용해서 지우개를 구현할건데, 캔버스 위에서 마우스 버튼을 누른 채로 이동하면 지워지도록 해야겠죠?

따라서 마우스를 따라다니면서 clearRect() 함수를 호출하도록 하는 것과 마우스가 클릭 중일 때와 아닐 때를 구분하는 것이 핵심이 됩니다. 앞서 클론 코딩을 진행하신 분이라면 이미 브러쉬가 구현되어 있기 때문에 동일한 마우스 이벤트에서 지우개 로직을 구현하면 됩니다.

기본 화면 구성


브러쉬와 지우개만 있는 기본 화면입니다.

기존 프로젝트 중에서 필요없는 코드를 최대한 줄이고 브러쉬와 지우개만 남겼습니다.

이때 실질적인 지우개 동작은 아래 onMouseMove() 함수 내에서 이루어집니다. clearRect() 함수가 호출되는 조건에 유의하세요.

const canvas = document.getElementById("jsCanvas");
const ctx = canvas.getContext("2d");

function onMouseMove(event) {
    const x = event.offsetX;
    const y = event.offsetY;

    if(mode === erase){
        if(isMouseDown) {
            ctx.clearRect(x-width/2, y-height/2, width, height);
        }
    }
}

canvas.addEventListener("mousemove", onMouseMove);
  1. 마우스가 움직일 때마다 onMouseMove() 함수를 호출
  2. 현재 모드가 지우개 모드인지 확인
  3. 만약 지우개 모드라면, 마우스 버튼을 누르고 있는 상태인지 확인
  4. 만약 마우스 버튼을 누르고 있는 상태라면, 마우스 위치에서 clearRect() 수행

TIP) event로 부터 현재 좌표를 가져올 때 offsetX 뿐 아니라 여러가지 좌표 옵션이 있습니다. 필요에 따라 적절한 좌표를 event로부터 가져와 사용할 수 있습니다.

전체 코드 보기

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="style.css" />
    <title>PaintJS</title>
</head>
<body>
    <canvas id="jsCanvas" class="canvas"></canvas>
    <div class="controls__btns">
        <button id="jsBrush">브러쉬</button>
        <button id="jsErase">지우개</button>
        <input type="range" id="jsRange" min="0.1" max="20.0" step="0.1"/>
    </div>
    <script src="app.js"></script>
</body>
</html>

CSS

/* sytle.css */
.canvas {
    width: 500px;
    height: 500px;
    background-color: white;
    border-radius: 15px;
    box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0px 0px 3px rgba(0, 0, 0, 0.5);
}
.controls__btns button {
    background-color: white;
    padding: 5px 10px;
    text-align: center;
    border-radius: 5px;
    box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
    border: 2px solid rgba(0, 0, 0, 0.2);
    color: rgba(0, 0, 0, 0.7);
}

JavaScript

// app.js
const canvas = document.getElementById("jsCanvas");
const ctx = canvas.getContext("2d");
const brush = document.getElementById("jsBrush");
const erase = document.getElementById("jsErase");
const range = document.getElementById("jsRange");

const INITIAL_COLOR = "#2c2c2c";
const INITIAL_LINEWIDTH = 5.0;
const CANVAS_SIZE = 500;

ctx.strokeStyle = INITIAL_COLOR;
ctx.fillStyle = INITIAL_COLOR;
ctx.lineWidth = INITIAL_LINEWIDTH;
canvas.width = CANVAS_SIZE;
canvas.height = CANVAS_SIZE;

const MODE_BUTTON = [brush, erase];
let mode = brush;
let painting = false;

function startPainting() { painting = true; }
function stopPainting() { painting = false; }

function onMouseMove(event) {
    const x = event.offsetX;
    const y = event.offsetY;
    if(mode === brush){
        if(!painting) {
            ctx.beginPath();
            ctx.moveTo(x, y);
        }
        else {
            ctx.lineTo(x, y);
            ctx.stroke();
        }
    }
    else if(mode === erase){
        if(painting) {
            ctx.clearRect(x-ctx.lineWidth/2, y-ctx.lineWidth/2, ctx.lineWidth, ctx.lineWidth);
        }
    }
}

function handleModeChange(event) {
    mode = event.target;
    // Button Highlight
    for(i = 0 ; i < MODE_BUTTON.length ; i++){
        var button = MODE_BUTTON[i];
        if(button === mode){
            button.style.backgroundColor = "skyblue";
        }
        else {
            button.style.backgroundColor = "white";
        }
    }
}

function handleRangeChange(event) {
    const size = event.target.value;
    ctx.lineWidth = size;
    range.value = size;
}

if (canvas) {
    canvas.addEventListener("mousemove", onMouseMove);
    canvas.addEventListener("mousedown", startPainting);
    canvas.addEventListener("mouseup", stopPainting);
    canvas.addEventListener("mouseleave", stopPainting);
}

MODE_BUTTON.forEach(mode 
	=> mode.addEventListener("click", handleModeChange)
);
range.addEventListener("input", handleRangeChange);

0개의 댓글