css
body { font-family: Verdana, Geneva, Tahoma, sans-serif; background: #aaa; height: 100vh; display: flex; justify-content: center; align-items: center; }
.menu { display: flex; justify-content: space-around; align-items: center; background: #333; color: #fff; padding: 10px; border-radius: 10px 10px 0 0; }
canvas { background: #fff; border-radius: 0 0 10px 10px; }
input { width: 50px; height: 30px; text-align: center; outline: none; border: none; }
.item { display: flex; align-items: center; column-gap: 10px; }
button { height: 35px; padding: 0 15px; border-radius: 7px; }
button:hover { background: powderblue; }
html
<div class="container">
<div class="menu">
<div class="item">
<label for="color">Color</label>
<input type="color" name="" id="color" />
</div>
<div class="item">
<label for="size">Size</label>
<input type="number" name="" id="size" min="1" max="10" value="5" />
</div>
<button class="clear">Clear</button>
<button class="save">Save to Image</button>
</div>
<canvas width="600" height="400"></canvas>
</div>
js
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d"); // 2d ๊ทธ๋ฆผ์ ๊ทธ๋ฆด ์ ์๋ ๊ณต๊ฐ ์ค์
const size = document.querySelector("#size");
const color = document.querySelector("#color");
const clear = document.querySelector(".clear");
const save = document.querySelector(".save");
let isPainting = false; // ๊ทธ๋ฆผ๊ทธ๋ฆฌ๋ ์ค์ธ์ง ์๋์ง ํ๋จํ๋ ๋ณ์
let lineSize = 5; // ๋ผ์ธ ๋๊ป 5๋ก ์์
// ๋ฐฐ๊ฒฝ์ ๋ฐ๊พธ๊ธฐ ์ํด ์ ์ธ
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// save. ์ด๋ฏธ์ง๋ก ์ ์ฅ
//blob - binary large object(์ฉ๋์ด ํฐ ํ์ผ์ ์๋ฏธ)
save.addEventListener("click", () => {
canvas.toBlob((blob) => { // toBlob(); - ํ์ผ์ ์ ์ฅํ๋ ๋ฉ์๋
const a = document.createElement("a"); // ์๋ aํ๊ทธ๋ฅผ ์์ฑ
a.href = URL.createObjectURL(blob); // ํ์ผ์ ๊ฐ์ URL ์ฃผ์๋ฅผ ๋ง๋ค์ด ์ค
a.download = "drawing.jpg"; // ์ ์ฅํ ํ์ผ ์ด๋ฆ ์ค์ (png ํฌ๋ช
์ผ๋ก๋ ๊ฐ๋ฅ - ๋ฐฐ๊ฒฝ์ ์ค์ ์ ํ์ ์)
a.click();
// console.log(a);
});
});
// clear
clear.addEventListener("click", () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// (์์์์น x, ์์์์น y, ์์ธ ํฌ๊ธฐ ๊ฐ๋ก, ์ง์ธ ํฌ๊ธฐ ์ธ๋ก)
});
// ์ ๊ตต๊ธฐ ์ ์ฉ
size.addEventListener("change", (event) => {
// console.log(event.target.value);
lineSize = event.target.value; // ์ ํํ ์ซ์ // *
});
// ์์ ์ ์ฉ
color.addEventListener("change", (event) => {
// console.log(event.target.value); // hex๊ฐ์ด ๋์ด
ctx.strokeStyle = event.target.value;
});
canvas.addEventListener("mousedown", (event) => {
// console.log(event);
isPainting = true; // ๊ทธ๋ฆผ ๊ทธ๋ฆฌ๋ ์ค
ctx.beginPath(); // ์๋ก์ด ๊ฒฝ๋ก ์์ฑ, ๊ทธ๋ฆฌ๊ธฐ ์์
ctx.moveTo(event.offsetX, event.offsetY); // ๋ง์ฐ์ค ์์น ์ขํ๊ฐ
});
canvas.addEventListener("mousemove", () => {
// ๋ง์ฐ์ค๋ฅผ ๋๋ฅด์ง ์์์ ๋๋ ์๋x
if (!isPainting) {
return;
}
ctx.lineWidth = lineSize; // *
ctx.lineCap = "round";
ctx.lineJoin = "round"; //์ ๊บฝ์ด๋ ์คํ์ผ
ctx.lineTo(event.offsetX, event.offsetY); // ์ ์ ๊ทธ๋ฆผ
ctx.stroke(); // ์ค๊ณฝ์ ์ ๊ทธ๋ฆผ
});
canvas.addEventListener("mouseup", (event) => {
isPainting = false; //๊ทธ๋ฆฌ๊ธฐ ๋
});
// ๋ง์ฐ์ค๊ฐ ํ๋ฉด ๋ฐ์ผ๋ก ๋ฒ์ด๋๋ฉด ๋ฉ์ถ๊ฒ ํจ
canvas.addEventListener("mouseout", (event) => {
isPainting = false; // ๊ทธ๋ฆฌ๊ธฐ ๋
});