Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

노마드코더 JS로 그림앱 만들기) #2 PAINTING BOARD - mousemove, mouseup, mousedown, mouseleave, change, dataset 본문

Javascript/노마드코더 - JS로 그림앱 만들기

노마드코더 JS로 그림앱 만들기) #2 PAINTING BOARD - mousemove, mouseup, mousedown, mouseleave, change, dataset

끈기JK 2022. 10. 6. 10:05

-클릭시 x, y 좌표 얻어내기

아래 코드로 콘솔에서 클릭 위치 얻어낼 수 있다.

function onClick(event) {
    console.log(event);
}

canvas.addEventListener("click", onClick);

 

클릭시마다 (0, 0) 에서 시작되는 직선을 그릴 수 있다.

ctx.lineWidth = 2;

function onClick(event) {
    ctx.moveTo(0, 0);
    ctx.lineTo(event.offsetX, event.offsetY);
    ctx.stroke();
}

canvas.addEventListener("click", onClick);

 

addEventListener에서 "click"을 "mousemove"로 바꾸면 이렇게 나온다.

function onClick(event) {
    ctx.moveTo(0, 0);
    ctx.lineTo(event.offsetX, event.offsetY);
    ctx.stroke();
}

canvas.addEventListener("mousemove", onClick);

 

-움직일때 마다 색깔 바뀌게 하기

참고사이트 : https://flatuicolors.com/palette/tr

ctx.lineWidth = 2;

const colors = [
    "#cd84f1",
    "#ffcccc",
    "#ff4d4d",
    "#ffaf40",
    "#fffa65",

    "#c56cf0",
    "#ffb8b8",
    "#ff3838",
    "#ff9f1a",
    "#fff200"
]

function onClick(event) {
    ctx.beginPath();
    ctx.moveTo(0, 0);
    const color = colors[Math.floor(Math.random() * colors.length)]
    ctx.strokeStyle = color;
    ctx.lineTo(event.offsetX, event.offsetY);
    ctx.stroke();
}

canvas.addEventListener("mousemove", onClick);

 


 

-마우스 클릭하여 움직일때 그리기

EventListener의 "mousemove"는 마우스 움직일때, "mousedown"은 클릭한 상태, "mouseup"은 클릭을 뗀 상태를 나타낸다.

// 기본 코드
ctx.lineWidth = 2;
let isPainting = false;

function onMove(event) {
    if (isPainting) {
        ctx.lineTo(event.offsetX, event.offsetY);
        ctx.stroke();
        return;
    }
    ctx.moveTo(event.offsetX, event.offsetY);
}
function onMouseDown() {
    isPainting = true;
}
function onMouseUp() {
    isPainting = false;
}

canvas.addEventListener("mousemove", onMove);
canvas.addEventListener("mousedown", onMouseDown);
canvas.addEventListener("mouseup", onMouseUp);

 

그러나 위 코드는 버그가 있는데 마우스를 누른 채로 화면 밖에 나갔다가 떼고 다시 돌아오면 그림이 그려진다.

이유는 마우스가 나가서 떼도 "mouseup" 이 동작하지 않기 때문이다.

 

첫 줄 코드로 수정하거나(canvas → document), 둘째 줄 코드를 추가하면 된다.

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

전체 코드는 아래와 같다.

function onMove(event) {
    if (isPainting) {
        ctx.lineTo(event.offsetX, event.offsetY);
        ctx.stroke();
        return;
    }
    ctx.moveTo(event.offsetX, event.offsetY);
}
function startPainting() {
    isPainting = true;
}
function cancelPainting() {
    isPainting = false;
}

canvas.addEventListener("mousemove", onMove);
canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", cancelPainting);
canvas.addEventListener("mouseleave", cancelPainting);

 


 

-선 굵기 바꾸기

 

// index.html 수정
<input id="line-width" type="range" min="1" max="10" value="5" />  // <body> 태그 내에 추가

addEventListener("change", ) 이용하고 console.log(event.target.value)로 바를 움직일때 마다 console에 값이 찍힌다.

const lineWidth = document.getElementById("line-width");

function onLineWidthChange(event) {
    console.log(event.target.value);
}

lineWidth.addEventListener("change", onLineWidthChange);

전체 코드

// app.js 수정
const lineWidth = document.getElementById("line-width");  // 추가
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 800;
ctx.lineWidth = lineWidth.value;  // 수정
let isPainting = false;

function onMove(event) {
    if (isPainting) {
        ctx.lineTo(event.offsetX, event.offsetY);
        ctx.stroke();
        return;
    }
    ctx.beginPath();  // 추가. 기존 선과의 연결을 끊기 위해
    ctx.moveTo(event.offsetX, event.offsetY);
}
function startPainting() {
    isPainting = true;
}
function cancelPainting() {
    isPainting = false;
}

function onLineWidthChange(event) {  // 추가
    ctx.lineWidth = event.target.value;
}

canvas.addEventListener("mousemove", onMove);
canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", cancelPainting);
canvas.addEventListener("mouseleave", cancelPainting);

lineWidth.addEventListener("change", onLineWidthChange);  // 추가

 

1씩 조정되는 것을 HTML 에서 step으로 0.1씩 줄 수 있다.

// index.html 수정
<input id="line-width" type="range" min="1" max="10" value="5" step="0.1" />

 

 


 

-색깔 고르기

// index.html 수정
<input type="color" name="" id="color" />  // <body> 태그 내에 추가

// app.js 수정
const color = document.getElementById("color");

function onColorChange(event) {
    console.log(event.target.value);
}

color.addEventListener("change", onColorChange);

색깔 선택하면 콘솔에 이런식으로 나온다.

 

전체 코드

// app.js 수정
const color = document.getElementById("color");  // 추가

function onColorChange(event) {  // 추가
    ctx.strokeStyle = event.target.value;
    ctx.fillStyle = event.target.value;
}

color.addEventListener("change", onColorChange);  // 추가

 


 

-색상에 옵션 주기

색상을 선택하도록 하기 위해 아래 코드 <body> 태그에 추가한다.

// index.html 수정
<div class="color-option" style="background-color: #1abc9c;" data-color="#1abc9c"></div>
<div class="color-option" style="background-color: #2ecc71;" data-color="#2ecc71"></div>
<div class="color-option" style="background-color: #3498db;" data-color="#3498db"></div>
<div class="color-option" style="background-color: #9b59b6;" data-color="#9b59b6"></div>
<div class="color-option" style="background-color: #34495e;" data-color="#34495e"></div>
<div class="color-option" style="background-color: #f1c40f;" data-color="#f1c40f"></div>
<div class="color-option" style="background-color: #e67e22;" data-color="#e67e22"></div>
<div class="color-option" style="background-color: #ecf0f1;" data-color="#ecf0f1"></div>
<div class="color-option" style="background-color: #95a5a6;" data-color="#95a5a6"></div>

data-color : 'data-문자' 로 입력하여 정보 넣을 수 있다.

data-potato로 입력하면 consolr.dir에서 이렇게 나온다.

 

CSS에 아래 코드 추가한다.

cursor: pointer 입력시 마우스 올리면 손모양으로 바뀐다. progress, wait, grab, grabbing, zoom-in, zoom-out 등이 있다.

url('image.png') 처럼 이미지를 커서로 사용할 수 있다.

// style.css 수정
.color-option {  // 추가
    width: 50px;
    height: 50px;
    cursor: pointer;
}


첫 줄 코드로 인자 받으면 HTML Collection으로 받는것이다.

배열로 받기 위해 둘째 줄 코드 Array.from 사용한다. forEach 사용을 위해서이다.

const colorOptions = document.getElementsByClassName("color-option");
//
const colorOptions = Array.from(document.getElementsByClassName("color-option"));

아래 코드를 입력한다.

const colorOptions = Array.from(document.getElementsByClassName("color-option"));

function onColorClick(event) {
    console.log(event.target);
}

colorOptions.forEach(color => color.addEventListener("click", onColorClick));

콘솔로 정보 받으면 다음과 같이 나온다.

 

위 정보는 부족하여 console.dir를 사용한다.

function onColorClick(event) {
    console.dir(event.target);
}

내리다 보면 dataset이 나오는데 여기서 color 정보가 나온다.

 

이런식으로 색상 정보 얻어올 수 있다.

function onColorClick(event) {
    console.dir(event.target.dataset.color);
}

 

클릭시 색상을 변경하는 코드

function onColorClick(event) {
    const colorValue = event.target.dataset.color;
    ctx.strokeStyle = colorValue;
    ctx.fillStyle = colorValue;
    color = colorValue;  // 색상 클릭시 색상이 변경되었다는 정보를 표현
}

 

전체 코드

// app.js 수정
const colorOptions = Array.from(document.getElementsByClassName("color-option"));  // 추가

function onColorClick(event) {  // 추가
    const colorValue = event.target.dataset.color;
    ctx.strokeStyle = colorValue;
    ctx.fillStyle = colorValue;
    color.value = colorValue;
}

colorOptions.forEach(color => color.addEventListener("click", onColorClick));  // 추가

 


 

-채우기 or 그리기 선택 버튼 만들기

 

// index.html 수정    
<button id="mode-btn">Fill</button>

아래 코드를 추가하면 Fill 모드일때 캔버스에 색상이 채워진다.

// app.js 추가
const modeBtn = document.getElementById("mode-btn");
let isFilling = false;

function onModeClick() {
    if (isFilling) {
        isFilling = false;
        modeBtn.innerText = "Fill";
    } else {
        isFilling = true;
        modeBtn.innerText = "Draw";
    }
}

function onCanvasClick() {
    if (isFilling) {
        ctx.fillRect(0, 0, 800, 800);
    }
}

canvas.addEventListener("click", onCanvasClick);

 


 

-화면 모두 지우기 버튼 만들기

// index.html 추가
<button id="destroy-btn">Destory</button>

CANVAS_WIDTH, CANVAS_HEIGHT 상수화하여 함수를 수정한다.

// app.js 추가
const destoryBtn = document.getElementById("destroy-btn");

const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 800;

function onCanvasClick() {  
    if (isFilling) {
        ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);  // 수정
    }
}

function onDestroyClick() {
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
}

destoryBtn.addEventListener("click", onDestroyClick);

 

 

-지우개 버튼 만들기

// index.html 추가
<button id="erase-btn">Erase</button>

// app.js 수정
const eraseBtn = document.getElementById("erase-btn");

function onEraserClick() {
    ctx.strokeStyle = "white";
    isFilling = false;
    modeBtn.innerText = "Fill";
}

eraseBtn.addEventListener("click", onEraserClick);

 


 

전체 코드

// app.js
const eraseBtn = document.getElementById("erase-btn");
const destoryBtn = document.getElementById("destroy-btn");
const modeBtn = document.getElementById("mode-btn");
const colorOptions = Array.from(document.getElementsByClassName("color-option"));
const color = document.getElementById("color");
const lineWidth = document.getElementById("line-width");
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 800;

canvas.width = 800;
canvas.height = 800;
ctx.lineWidth = lineWidth.value;
let isPainting = false;
let isFilling = false;

function onMove(event) {
    if (isPainting) {
        ctx.lineTo(event.offsetX, event.offsetY);
        ctx.stroke();
        return;
    }
    ctx.beginPath();
    ctx.moveTo(event.offsetX, event.offsetY);
}
function startPainting() {
    isPainting = true;
}
function cancelPainting() {
    isPainting = false;
}

function onLineWidthChange(event) {
    ctx.lineWidth = event.target.value;
}

function onColorChange(event) {
    ctx.strokeStyle = event.target.value;
    ctx.fillStyle = event.target.value;
}

function onColorClick(event) {
    const colorValue = event.target.dataset.color;
    ctx.strokeStyle = colorValue;
    ctx.fillStyle = colorValue;
    color.value = colorValue;
}

function onModeClick() {
    if (isFilling) {
        isFilling = false;
        modeBtn.innerText = "Fill";
    } else {
        isFilling = true;
        modeBtn.innerText = "Draw";
    }
}

function onCanvasClick() {
    if (isFilling) {
        ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
    }
}

function onDestroyClick() {
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
}

function onEraserClick() {
    ctx.strokeStyle = "white";
    isFilling = false;
    modeBtn.innerText = "Fill";
}

canvas.addEventListener("mousemove", onMove);
canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", cancelPainting);
canvas.addEventListener("mouseleave", cancelPainting);
canvas.addEventListener("click", onCanvasClick);

lineWidth.addEventListener("change", onLineWidthChange);
color.addEventListener("change", onColorChange);

colorOptions.forEach(color => color.addEventListener("click", onColorClick));

modeBtn.addEventListener("click", onModeClick);
destoryBtn.addEventListener("click", onDestroyClick);
eraseBtn.addEventListener("click", onEraserClick);