개발자입니다
노마드코더 JS로 그림앱 만들기) #2 PAINTING BOARD - mousemove, mouseup, mousedown, mouseleave, change, dataset 본문
노마드코더 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);