Hey, I added lines!

This commit is contained in:
Yaro Kasear 2025-12-04 16:15:58 -06:00
parent 285db679d9
commit 55f18b1cbe

View file

@ -63,6 +63,11 @@
<path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2z" /> <path d="M0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2z" />
</svg> </svg>
</label> </label>
<input type="radio" class="btn-check" id="line" name="tool">
<label for="line" class="btn btn-sm btn-light border d-inline-flex align-items-center justify-content-center">
</label>
</div> </div>
</div> </div>
<span id="dot" class="position-absolute p-0 m-0 d-none"> <span id="dot" class="position-absolute p-0 m-0 d-none">
@ -89,12 +94,17 @@
let dpr = 1; let dpr = 1;
let selectedColor = '#000000'; let selectedColor = '#000000';
let currentRect = null; let currentShape = null;
let rects = []; let shapes = [];
resizeAndSetupCanvas(); resizeAndSetupCanvas();
window.addEventListener('resize', resizeAndSetupCanvas); window.addEventListener('resize', resizeAndSetupCanvas);
function getActiveTool() {
const checked = document.querySelector('input[name="tool"]:checked');
return checked ? checked.id : 'outline';
}
function snapToGrid(x, y) { function snapToGrid(x, y) {
const rect = gridEl.getBoundingClientRect(); const rect = gridEl.getBoundingClientRect();
const clampedX = Math.min(Math.max(x, rect.left), rect.right); const clampedX = Math.min(Math.max(x, rect.left), rect.right);
@ -113,14 +123,20 @@
}; };
} }
function normalizeRect(rect) { function normalizeRect(shape) {
const x = Math.min(shape.x1, shape.x2);
const y = Math.min(shape.y1, shape.y2);
const w = Math.abs(shape.x2 - shape.x1);
const h = Math.abs(shape.y2 - shape.y1);
return { return {
x: Math.min(rect.x1, rect.x2), type: 'rect',
y: Math.min(rect.y1, rect.y2), x,
w: Math.abs(rect.x2 - rect.x1), y,
h: Math.abs(rect.y2 - rect.y1), w,
color: rect.color, h,
fill: rect.fill color: shape.color,
fill: shape.fill
}; };
} }
@ -141,20 +157,29 @@
if (!ctx) return; if (!ctx) return;
clearCanvas(); clearCanvas();
rects.forEach(drawRect); shapes.forEach(drawShape);
} }
function drawRect(rect) { function drawShape(shape) {
if (!ctx) return;
ctx.save(); ctx.save();
ctx.strokeStyle = shape.color || '#000000';
ctx.strokeStyle = rect.color; if (shape.type === 'rect') {
ctx.strokeRect(rect.x, rect.y, rect.w, rect.h); ctx.strokeRect(shape.x, shape.y, shape.w, shape.h);
if (rect.fill) { if (shape.fill) {
ctx.globalAlpha = 0.15; ctx.globalAlpha = 0.15;
ctx.fillStyle = rect.color; ctx.fillStyle = shape.color;
ctx.fillRect(rect.x, rect.y, rect.w, rect.h); ctx.fillRect(shape.x, shape.y, shape.w, shape.h);
ctx.globalAlpha = 1; ctx.globalAlpha = 1;
}
} else if ( shape.type === 'line' ) {
ctx.beginPath();
ctx.moveTo(shape.x1, shape.y1);
ctx.lineTo(shape.x2, shape.y2);
ctx.stroke();
} }
ctx.restore(); ctx.restore();
@ -177,12 +202,12 @@
if ((e.ctrlKey || e.metaKey) && key === 'z') { if ((e.ctrlKey || e.metaKey) && key === 'z') {
e.preventDefault(); e.preventDefault();
rects.pop(); shapes.pop();
redrawAll(); redrawAll();
} }
if (key === 'escape' && currentRect) { if (key === 'escape' && currentShape) {
currentRect = null; currentShape = null;
redrawAll(); redrawAll();
} }
}); });
@ -201,18 +226,36 @@
dotEl.style.top = `${renderY}px`; dotEl.style.top = `${renderY}px`;
dotEl.style.left = `${renderX}px`; dotEl.style.left = `${renderX}px`;
if (currentRect) { if (currentShape) {
const previewRect = normalizeRect({ const tool = currentShape.tool;
...currentRect,
x2: snapX,
y2: snapY
});
clearCanvas(); clearCanvas();
rects.forEach(drawRect); shapes.forEach(drawShape);
ctx.save();
ctx.setLineDash([5, 3]); ctx.setLineDash([5, 3]);
drawRect(previewRect);
if (tool === 'line') {
const previewLine = {
type: 'line',
x1: currentShape.x1,
y1: currentShape.y1,
x2: snapX,
y2: snapY,
color: currentShape.color
};
drawShape(previewLine);
} else {
const previewRect = normalizeRect({
...currentShape,
x2: snapX,
y2: snapY
});
drawShape(previewRect);
}
ctx.setLineDash([]); ctx.setLineDash([]);
ctx.restore();
} }
}); });
@ -228,34 +271,66 @@
if (e.target.closest('#toolBar')) return; if (e.target.closest('#toolBar')) return;
const {ix, iy, x: snapX, y: snapY} = snapToGrid(e.clientX, e.clientY); const {x: snapX, y: snapY} = snapToGrid(e.clientX, e.clientY);
const tool = getActiveTool();
currentRect = { if (tool === 'line') {
x1: snapX, currentShape = {
y1: snapY, tool,
x2: snapX, type: 'line',
y2: snapY, x1: snapX,
color: selectedColor, y1: snapY,
fill: document.getElementById('filled').checked x2: snapX,
}; y2: snapY,
color: selectedColor
};
} else {
currentShape = {
tool,
x1: snapX,
y1: snapY,
x2: snapX,
y2: snapY,
color: selectedColor,
fill: document.getElementById('filled').checked
};
}
}); });
window.addEventListener('mouseup', (e) => { window.addEventListener('mouseup', (e) => {
if (!currentRect) return; if (!currentShape) return;
const {ix, iy, x: snapX, y: snapY } = snapToGrid(e.clientX, e.clientY); const { x: snapX, y: snapY } = snapToGrid(e.clientX, e.clientY);
currentRect.x2 = snapX; currentShape.x2 = snapX;
currentRect.y2 = snapY; currentShape.y2 = snapY;
const finalRect = normalizeRect(currentRect); let finalShape = null;
if (finalRect.w > 0 && finalRect.h > 0) { if (currentShape.tool === 'line') {
rects.push(finalRect); finalShape = {
type: 'line',
x1: currentShape.x1,
y1: currentShape.y1,
x2: currentShape.x2,
y2: currentShape.y2,
color: currentShape.color
};
} else {
const rect = normalizeRect(currentShape);
if (rect.w > 0 && rect.h > 0) {
finalShape = rect;
}
} }
clearCanvas();
rects.forEach(drawRect);
currentRect = null; if (finalShape) {
shapes.push(finalShape);
}
clearCanvas();
shapes.forEach(drawShape);
currentShape = null;
}); });
{% endblock %} {% endblock %}