반응형
강의에서 배운 기능에 새로운 기능인 eraser와 clear 버튼을 추가 했다.
그리고 컬러는 슬라이드로 구현을 해놨다.
아예 처음부터 만들기도 했고 공부한지 시간이 꽤 지났어서 오래 걸렸다. ㅠㅠ
클론코딩은 역시 본인이 직접 다시 만들어봐야 하는 것 같다.
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">
<title>Paint JS</title>
</head>
<body>
<section class="canvas-wrap">
<canvas class="jsCanvas canvas" width="500" height="700"></canvas>
<nav class="menu">
<button class="menu__icon" type="menu" value="open" onclick="activeMenu()">
<span></span>
<span></span>
<span></span>
</button>
<ul class="controls-colors">
<li class="controls__color jsColor" style="background-color: #333;"></li>
<li class="controls__color jsColor" style="background-color: #fff;"></li>
<li class="controls__color jsColor" style="background-color: #fe3629;"></li>
<li class="controls__color jsColor" style="background-color: #FE8B00;"></li>
<li class="controls__color jsColor" style="background-color: #FEC600;"></li>
<li class="controls__color jsColor" style="background-color: #44D558;"></li>
<li class="controls__color jsColor" style="background-color: #52C1FB;"></li>
<li class="controls__color jsColor" style="background-color: #006BF9"></li>
<li class="controls__color jsColor" style="background-color: #8040d4"></li>
</ul>
</nav>
</section>
<div class="controls-functions">
<div class="controls-btns">
<button class="btns__clear jsClear">CLEAR</button>
<button class="btns__save jsSave">SAVE</button>
</div>
<div class="controls-tools">
<button class="tools__pen jsPen">PEN</button>
<button class="tools__eraser jsEraser">ERASER</button>
<button class="tools__paint jsPaint">PAINT</button>
<input type="range" class="tools__range jsRange" min="0.1" max="10" value="5" step="0.1">
</div>
</div>
<script src="canvas.js"></script>
<script src="menu.js"></script>
</body>
</html>
CSS
@import "reset.css";
body {
padding: 50px 0px;
background-color: #f6f9fc;
}
.canvas-wrap {
display: flex;
justify-content: center;
align-items: center;
height: 70vh;
margin-bottom: 2em;
}
.canvas {
width: 500px;
height: 700px;
background-color: #fff;
border-radius: 15px;
box-shadow: 0px 4px 5px rgb(233, 233, 233), 0 1px 3px rgb(160, 160, 160);
}
/* ==================== colors menu */
.menu {
position: relative;
height: 100%;
margin-left: 60px;
}
.menu__icon {
position: absolute;
top: 6%;
left: .5em;
width: 42px;
height: 32px;
z-index: 10;
cursor: pointer;
background-color: unset;
border: none;
transition: transform 1s;
}
.menu__icon span {
position: absolute;
left: 0;
width: 100%;
height: 2px;
background-color: #444;
}
.menu__icon span:nth-of-type(1) {top: 0;}
.menu__icon span:nth-of-type(2) {top: 15px; height: 3px;}
.menu__icon span:nth-of-type(3) {bottom: 0;}
.controls-colors {
display: flex;
flex-direction: column;
position: absolute;
top: -40%;
left: 0;
opacity: 0;
transform: translateY(-40%);
transition: 1s;
}
.controls__color {
width: 50px;
height: 50px;
margin-top: 15px;
border-radius: 30px 40px 5px;
cursor: pointer;
box-shadow: 0px 4px 5px rgb(233, 233, 233), 0 1px 3px rgb(160, 160, 160);
}
.controls-color:active {
transform: scale(0.98);
}
/* ======================= function wrap */
.controls-functions {
display: flex;
flex-flow: column wrap;
align-items: center;
margin-right: 8em;
}
.controls-functions button {
width: 80px;
height: 40px;
margin: .5em .1em;
font-size: inherit;
background-color: #fff;
border: 1px solid rgb(235, 235, 235);
border-radius: .3em;
box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.05);
cursor: pointer;
}
.tools__range {
width: 160px;
margin-left: 50px;
}
JavaScript
저번에 올렸을 때 이웃님이 Array.from을 사용하는 것은 functional 하지 않다고 해주셔서 그냥 forEach()로 돌렸다.
어차피 querySelectorAll로 받으면 배열로 가져올텐데 굳이 새로운 배열로 만들어주지않아도 될 것 같았다.
const canvas = document.querySelector(".jsCanvas");
const penBtn = document.querySelector(".jsPen");
const eraserBtn = document.querySelector(".jsEraser");
const paintBtn = document.querySelector(".jsPaint");
const range = document.querySelector(".jsRange");
const clearBtn = document.querySelector(".jsClear");
const saveBtn = document.querySelector(".jsSave");
const colors = document.querySelectorAll(".jsColor");
const ctx = canvas.getContext('2d');
const CANVAS_WEIGHT = 500;
const CANVAS_HEIGHT = 700;
// canvas setting
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, CANVAS_WEIGHT, CANVAS_HEIGHT);
ctx.lineWidth = 2.5;
ctx.fillStyle = "#2c2c2c";
// default = false
let painting,
filling,
erasing = false;
function startPainting() {
painting = true;
erasing = true;
}
function stopPainting() {
painting = false;
erasing = false;
}
/* drawing stroke */
function onMouseMove(e) {
const x = e.offsetX;
const y = e.offsetY;
if (painting) {
ctx.lineTo(x, y);
ctx.stroke();
} else {
ctx.beginPath();
ctx.moveTo(x, y); // 마우스를 따라간 곳이 시작점
}
}
// choose color = drawing color
function clickColor(e) {
const color = e.target.style.backgroundColor;
ctx.strokeStyle = color;
ctx.fillStyle = color;
}
/* choose color
배열에 담아 forEach() 돌려줌 */
colors.forEach((targetColor) => {
targetColor.addEventListener("click", clickColor);
})
// handle range
function handleRange(e) {
const rangeSize = e.target.value;
ctx.lineWidth = rangeSize;
}
function clickPenBtn() {
ctx.globalCompositeOperation = 'source-over';
filling = false;
}
function clickPaintBtn() {
ctx.globalCompositeOperation = 'source-over';
filling = true;
}
// fill canvas
function handleCanvasClick(e) {
if (filling) ctx.fillRect(0, 0, CANVAS_HEIGHT, CANVAS_HEIGHT);
}
function clickEraserBtn() {
ctx.globalCompositeOperation = 'destination-out';
ctx.strokeStyle = "rgb(255, 255, 255)";
}
function clickClearBtn() {
ctx.clearRect(0, 0, CANVAS_WEIGHT, CANVAS_WEIGHT);
ctx.beginPath();
}
function clickSaveBtn() {
const img = canvas.toDataURL();
const link = document.createElement("a"); // a: anchar (href link같은거)
link.href = img;
link.download = "canvas image";
link.click();
}
/* 1. 마우스가 돌아다닐 때
2. 마우스가 클릭 될 때
3. 마우스가 떨어질 때
4. 마우스가 캔버스 밖에 있을 때 */
if (canvas) {
canvas.addEventListener("mousemove", onMouseMove);
canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", stopPainting);
canvas.addEventListener("mouseleave", stopPainting);
canvas.addEventListener("click", handleCanvasClick);
}
if (penBtn) penBtn.addEventListener("click", clickPenBtn);
if (paintBtn) paintBtn.addEventListener("click", clickPaintBtn);
if (eraserBtn) eraserBtn.addEventListener("click", clickEraserBtn);
if (clearBtn) clearBtn.addEventListener("click", clickClearBtn);
if (saveBtn) saveBtn.addEventListener("click", clickSaveBtn);
if (range) range.addEventListener("input", handleRange);
반응형