728x90
그림판
선,원,사각형 및 레이어 있는 그림판
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>그림판</title>
<style>
*{
box-sizing:border-box;
}
body{
margin:0;
font-family:Arial,sans-serif;
background:#dfe4ea;
overflow:hidden;
}
.topbar{
display:flex;
flex-wrap:wrap;
gap:10px;
padding:10px;
background:#222;
color:white;
align-items:center;
}
button{
border:none;
padding:8px 14px;
border-radius:6px;
cursor:pointer;
background:#444;
color:white;
}
button.active{
background:#1e90ff;
}
.toggleBtn{
background:#111;
padding:4px 10px;
font-size:12px;
}
.layout{
display:flex;
height:calc(100vh - 70px);
}
.sidebar{
width:240px;
background:#2f3542;
color:white;
padding:10px;
overflow:auto;
}
.title{
font-size:18px;
margin-bottom:10px;
font-weight:bold;
}
.layerItem{
background:#57606f;
padding:10px;
border-radius:6px;
margin-bottom:8px;
cursor:pointer;
}
.layerItem.active{
background:#1e90ff;
}
.canvasWrap{
flex:1;
overflow:auto;
background:#b2bec3;
}
#canvasContainer{
position:relative;
width:1400px;
height:900px;
background:white;
margin:20px auto;
border:2px solid #333;
}
canvas{
position:absolute;
left:0;
top:0;
}
input[type="color"]{
width:50px;
height:40px;
border:none;
cursor:pointer;
}
</style>
</head>
<body>
<div class="topbar">
<button data-tool="pen" class="active">펜</button>
<button data-tool="line">선</button>
<button data-tool="rect">사각형</button>
<button data-tool="circle">원</button>
<button data-tool="eraser">지우개</button>
색상
<input type="color" id="colorPicker" value="#000000">
굵기
<input type="range" id="sizePicker" min="1" max="50" value="3">
<button id="addLayerBtn">레이어 추가</button>
<button id="saveBtn">PNG 저장</button>
<input type="file" id="imageLoader" accept="image/*">
</div>
<div class="layout">
<div class="sidebar">
<div class="title">레이어</div>
<div id="layerList"></div>
</div>
<div class="canvasWrap">
<div id="canvasContainer"></div>
</div>
</div>
<script>
const WIDTH = 1400;
const HEIGHT = 900;
const canvasContainer = document.getElementById("canvasContainer");
const layerList = document.getElementById("layerList");
const colorPicker = document.getElementById("colorPicker");
const sizePicker = document.getElementById("sizePicker");
const addLayerBtn = document.getElementById("addLayerBtn");
const saveBtn = document.getElementById("saveBtn");
const imageLoader = document.getElementById("imageLoader");
const toolButtons = document.querySelectorAll("[data-tool]");
let currentTool = "pen";
let layers = [];
let activeLayerIndex = 0;
let drawing = false;
let startX = 0;
let startY = 0;
let savedImage = null;
toolButtons.forEach(btn=>{
btn.addEventListener("click",()=>{
toolButtons.forEach(b=>b.classList.remove("active"));
btn.classList.add("active");
currentTool = btn.dataset.tool;
});
});
function createLayer(name){
const canvas = document.createElement("canvas");
canvas.width = WIDTH;
canvas.height = HEIGHT;
canvas.style.zIndex = layers.length;
canvasContainer.appendChild(canvas);
const ctx = canvas.getContext("2d");
ctx.lineCap = "round";
ctx.lineJoin = "round";
const layer = {
name,
canvas,
ctx,
visible:true
};
layers.push(layer);
attachCanvasEvents(canvas);
refreshLayerUI();
}
function refreshLayerUI(){
layerList.innerHTML = "";
layers.forEach((layer,index)=>{
const div = document.createElement("div");
div.className = "layerItem";
if(index === activeLayerIndex){
div.classList.add("active");
}
div.innerHTML = `
<div style="
display:flex;
justify-content:space-between;
align-items:center;
gap:10px;
">
<span>${layer.name}</span>
<button class="toggleBtn">
${layer.visible ? "ON" : "OFF"}
</button>
</div>
`;
div.addEventListener("click",()=>{
activeLayerIndex = index;
refreshLayerUI();
});
const toggleBtn = div.querySelector(".toggleBtn");
toggleBtn.addEventListener("click",(e)=>{
e.stopPropagation();
layer.visible = !layer.visible;
layer.canvas.style.display =
layer.visible ? "block" : "none";
refreshLayerUI();
});
layerList.appendChild(div);
});
}
function getActiveLayer(){
return layers[activeLayerIndex];
}
function attachCanvasEvents(canvas){
canvas.addEventListener("mousedown",startDraw);
canvas.addEventListener("mousemove",draw);
canvas.addEventListener("mouseup",stopDraw);
canvas.addEventListener("mouseleave",stopDraw);
}
function startDraw(e){
drawing = true;
startX = e.offsetX;
startY = e.offsetY;
const ctx = getActiveLayer().ctx;
savedImage = ctx.getImageData(0,0,WIDTH,HEIGHT);
if(currentTool === "pen" || currentTool === "eraser"){
ctx.beginPath();
ctx.moveTo(startX,startY);
}
}
function draw(e){
if(!drawing) return;
const x = e.offsetX;
const y = e.offsetY;
const ctx = getActiveLayer().ctx;
ctx.lineWidth = sizePicker.value;
ctx.strokeStyle =
currentTool === "eraser"
? "#ffffff"
: colorPicker.value;
// 펜 / 지우개
if(currentTool === "pen" || currentTool === "eraser"){
ctx.lineTo(x,y);
ctx.stroke();
}
// 선
else if(currentTool === "line"){
ctx.putImageData(savedImage,0,0);
ctx.beginPath();
ctx.moveTo(startX,startY);
ctx.lineTo(x,y);
ctx.stroke();
}
// 사각형
else if(currentTool === "rect"){
ctx.putImageData(savedImage,0,0);
ctx.beginPath();
ctx.rect(
startX,
startY,
x-startX,
y-startY
);
ctx.stroke();
}
// 원
else if(currentTool === "circle"){
ctx.putImageData(savedImage,0,0);
const radius = Math.sqrt(
Math.pow(x-startX,2)+
Math.pow(y-startY,2)
);
ctx.beginPath();
ctx.arc(
startX,
startY,
radius,
0,
Math.PI*2
);
ctx.stroke();
}
}
function stopDraw(){
drawing = false;
}
addLayerBtn.addEventListener("click",()=>{
createLayer(
"레이어 " + (layers.length + 1)
);
});
saveBtn.addEventListener("click",()=>{
const exportCanvas =
document.createElement("canvas");
exportCanvas.width = WIDTH;
exportCanvas.height = HEIGHT;
const exportCtx =
exportCanvas.getContext("2d");
layers.forEach(layer=>{
if(layer.visible){
exportCtx.drawImage(
layer.canvas,
0,
0
);
}
});
const link = document.createElement("a");
link.download = "drawing.png";
link.href =
exportCanvas.toDataURL("image/png");
link.click();
});
imageLoader.addEventListener("change",(e)=>{
const file = e.target.files[0];
if(!file) return;
const reader = new FileReader();
reader.onload = function(event){
const img = new Image();
img.onload = function(){
const ctx = getActiveLayer().ctx;
ctx.drawImage(img,0,0);
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
});
// 기본 레이어 생성
createLayer("배경");
</script>
</body>
</html>728x90
'Software > JavaScript' 카테고리의 다른 글
| javascript시작하기 - 비트박스 (0) | 2026.05.13 |
|---|---|
| Javascript 시작하기 - 40음계 피아노 (0) | 2026.05.09 |
| Javascript 시작하기 - ThreeJS로 육각형 그려서 크기 바꾸기#1 (0) | 2026.02.08 |
| Javascript 시작하기 - ThreeJS로 선그리기 #1 (0) | 2026.02.08 |
| 음성 인식 & 출력 예제 #1 (0) | 2026.02.06 |
