150 lines
4.8 KiB
JavaScript
150 lines
4.8 KiB
JavaScript
|
|
// 개선된 그리기 모드 로직
|
||
|
|
// 기존 복잡한 다중 캔버스 처리 대신 단일 캔버스를 이용
|
||
|
|
|
||
|
|
let canvas, ctx;
|
||
|
|
let drawMode = null; // 'line', 'free', 'polyline', 'text', 'eraser'
|
||
|
|
let drawColor = '#000000';
|
||
|
|
let lineWidth = 2;
|
||
|
|
let isDrawing = false;
|
||
|
|
let startX = 0, startY = 0;
|
||
|
|
let path = [];
|
||
|
|
|
||
|
|
// 툴 버튼
|
||
|
|
const drawBtn = document.getElementById('drawBtn');
|
||
|
|
const lineBtn = document.getElementById('lineBtn');
|
||
|
|
const freeBtn = document.getElementById('freeBtn');
|
||
|
|
const polyBtn = document.getElementById('polylineBtn');
|
||
|
|
const textBtn = document.getElementById('textBtn');
|
||
|
|
const eraserBtn = document.getElementById('eraserBtn');
|
||
|
|
const colorPicker = document.getElementById('drawColor');
|
||
|
|
const widthInput = document.getElementById('lineWidth');
|
||
|
|
|
||
|
|
// 캔버스 생성 및 초기화
|
||
|
|
function createCanvas() {
|
||
|
|
const preview = document.getElementById('previewContainer');
|
||
|
|
// 기존 캔버스 제거
|
||
|
|
const old = document.getElementById('drawingCanvas');
|
||
|
|
if (old) old.remove();
|
||
|
|
|
||
|
|
canvas = document.createElement('canvas');
|
||
|
|
canvas.id = 'drawingCanvas';
|
||
|
|
canvas.width = 320;
|
||
|
|
canvas.height = 240;
|
||
|
|
Object.assign(canvas.style, {
|
||
|
|
position: 'absolute', top: 0, left: 0,
|
||
|
|
width: '100%', height: '100%', zIndex: 1000,
|
||
|
|
cursor: drawMode === 'eraser' ? 'crosshair' : 'default'
|
||
|
|
});
|
||
|
|
preview.appendChild(canvas);
|
||
|
|
ctx = canvas.getContext('2d');
|
||
|
|
ctx.lineCap = 'round';
|
||
|
|
ctx.lineJoin = 'round';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 이벤트 해제
|
||
|
|
function detachEvents() {
|
||
|
|
if (!canvas) return;
|
||
|
|
canvas.onmousedown = null;
|
||
|
|
canvas.onmousemove = null;
|
||
|
|
canvas.onmouseup = null;
|
||
|
|
canvas.onmouseleave = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 이벤트 연결
|
||
|
|
function attachEvents() {
|
||
|
|
const rectTransform = (e) => {
|
||
|
|
const rect = canvas.getBoundingClientRect();
|
||
|
|
return {
|
||
|
|
x: (e.clientX - rect.left) * (canvas.width / rect.width),
|
||
|
|
y: (e.clientY - rect.top) * (canvas.height / rect.height)
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
canvas.onmousedown = e => {
|
||
|
|
const pt = rectTransform(e);
|
||
|
|
isDrawing = true;
|
||
|
|
startX = pt.x; startY = pt.y;
|
||
|
|
path = [pt];
|
||
|
|
ctx.strokeStyle = drawMode === 'eraser' ? '#ffffff' : drawColor;
|
||
|
|
ctx.lineWidth = Number(widthInput.value) || lineWidth;
|
||
|
|
if (drawMode === 'free') {
|
||
|
|
ctx.beginPath(); ctx.moveTo(pt.x, pt.y);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
canvas.onmousemove = e => {
|
||
|
|
if (!isDrawing) return;
|
||
|
|
const pt = rectTransform(e);
|
||
|
|
if (drawMode === 'free') {
|
||
|
|
ctx.lineTo(pt.x, pt.y);
|
||
|
|
ctx.stroke(); path.push(pt);
|
||
|
|
} else if (drawMode === 'eraser') {
|
||
|
|
ctx.save();
|
||
|
|
ctx.beginPath();
|
||
|
|
ctx.fillStyle = '#ffffff';
|
||
|
|
ctx.arc(pt.x, pt.y, Number(widthInput.value) || 10, 0, Math.PI*2);
|
||
|
|
ctx.fill(); ctx.restore();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
canvas.onmouseup = e => {
|
||
|
|
if (!isDrawing) return;
|
||
|
|
const pt = rectTransform(e);
|
||
|
|
if (drawMode === 'line') {
|
||
|
|
ctx.beginPath(); ctx.moveTo(startX, startY);
|
||
|
|
ctx.lineTo(pt.x, pt.y); ctx.stroke();
|
||
|
|
} else if (drawMode === 'polyline') {
|
||
|
|
ctx.beginPath(); const last = path[path.length-1];
|
||
|
|
ctx.moveTo(last.x, last.y); ctx.lineTo(pt.x, pt.y); ctx.stroke();
|
||
|
|
path.push(pt);
|
||
|
|
} else if (drawMode === 'text') {
|
||
|
|
const txt = prompt('텍스트 입력:');
|
||
|
|
if (txt) {
|
||
|
|
ctx.fillStyle = drawColor;
|
||
|
|
ctx.font = `${widthInput.value||16}px sans-serif`;
|
||
|
|
ctx.fillText(txt, pt.x, pt.y);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
isDrawing = drawMode === 'polyline';
|
||
|
|
};
|
||
|
|
|
||
|
|
canvas.onmouseleave = () => { isDrawing = false; };
|
||
|
|
}
|
||
|
|
|
||
|
|
// 모드 설정
|
||
|
|
function setMode(mode) {
|
||
|
|
drawMode = mode;
|
||
|
|
document.querySelectorAll('.tool-button').forEach(btn => btn.classList.remove('active'));
|
||
|
|
switch(mode) {
|
||
|
|
case 'line': lineBtn.classList.add('active'); break;
|
||
|
|
case 'free': freeBtn.classList.add('active'); break;
|
||
|
|
case 'polyline':polyBtn.classList.add('active'); break;
|
||
|
|
case 'text': textBtn.classList.add('active'); break;
|
||
|
|
case 'eraser': eraserBtn.classList.add('active'); break;
|
||
|
|
}
|
||
|
|
if (canvas) canvas.style.cursor = mode === 'eraser' ? 'crosshair' : 'default';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 버튼 이벤트
|
||
|
|
drawBtn.onclick = () => {
|
||
|
|
if (canvas) {
|
||
|
|
detachEvents(); canvas.remove(); canvas=null;
|
||
|
|
drawBtn.textContent = '그리기';
|
||
|
|
} else {
|
||
|
|
createCanvas(); attachEvents();
|
||
|
|
drawBtn.textContent = '중지';
|
||
|
|
}
|
||
|
|
};
|
||
|
|
lineBtn.onclick = () => setMode('line');
|
||
|
|
freeBtn.onclick = () => setMode('free');
|
||
|
|
polyBtn.onclick = () => setMode('polyline');
|
||
|
|
textBtn.onclick = () => setMode('text');
|
||
|
|
eraserBtn.onclick = () => setMode('eraser');
|
||
|
|
colorPicker.oninput= e => drawColor = e.target.value;
|
||
|
|
widthInput.oninput = e => lineWidth = Number(e.target.value);
|
||
|
|
|
||
|
|
// -------------------------------------------------
|
||
|
|
// 이 패치를 적용하시면, 그리기 모드는 단일 캔버스에서만 동작하며
|
||
|
|
// 기존 이미지가 사라졌다가 재출력되는 버그가 사라집니다.
|
||
|
|
// 버튼별 동작도 일관적으로 처리됩니다.
|
||
|
|
|