Some nice refinements here.

This commit is contained in:
Yaro Kasear 2025-12-04 15:31:42 -06:00
parent 5b8f14c99b
commit 285db679d9

View file

@ -12,6 +12,8 @@
linear-gradient(to bottom, #ccc 1px, transparent 1px); linear-gradient(to bottom, #ccc 1px, transparent 1px);
background-size: var(--grid) var(--grid); background-size: var(--grid) var(--grid);
cursor: none; cursor: none;
height: 80vh;
width: 100%;
height: calc(round(nearest, 80vh, var(--grid)) + 1px); height: calc(round(nearest, 80vh, var(--grid)) + 1px);
width: calc(round(nearest, 100%, var(--grid)) + 1px); width: calc(round(nearest, 100%, var(--grid)) + 1px);
} }
@ -40,7 +42,7 @@
<div id="grid" class="position-relative overflow-hidden"> <div id="grid" class="position-relative overflow-hidden">
<div id="toolBar" <div id="toolBar"
class="btn-toolbar bg-light position-absolute border border-secondary-subtle rounded start-50 p-1"> class="btn-toolbar bg-light position-absolute border border-secondary-subtle rounded start-50 p-1">
<input type="color" class="form-control form-control-sm form-control-color" id="color" oninput="setColor()"> <input type="color" class="form-control form-control-sm form-control-color" id="color">
<div class="vr mx-1"></div> <div class="vr mx-1"></div>
<div class="btn-group"> <div class="btn-group">
<input type="radio" class="btn-check" id="outline" name="tool" checked> <input type="radio" class="btn-check" id="outline" name="tool" checked>
@ -77,12 +79,14 @@
{% block script %} {% block script %}
const canvasEl = document.getElementById('overlay'); const canvasEl = document.getElementById('overlay');
const colorEl = document.getElementById('color');
const coordsEl = document.getElementById('coords'); const coordsEl = document.getElementById('coords');
const dotEl = document.getElementById('dot'); const dotEl = document.getElementById('dot');
const dotSVGEl = document.getElementById('dotSVG'); const dotSVGEl = document.getElementById('dotSVG');
const gridEl = document.getElementById('grid'); const gridEl = document.getElementById('grid');
let ctx; let ctx;
let dpr = 1;
let selectedColor = '#000000'; let selectedColor = '#000000';
let currentRect = null; let currentRect = null;
@ -92,7 +96,7 @@
window.addEventListener('resize', resizeAndSetupCanvas); window.addEventListener('resize', resizeAndSetupCanvas);
function snapToGrid(x, y) { function snapToGrid(x, y) {
const rect = canvasEl.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);
const clampedY = Math.min(Math.max(y, rect.top), rect.bottom); const clampedY = Math.min(Math.max(y, rect.top), rect.bottom);
@ -115,12 +119,13 @@
y: Math.min(rect.y1, rect.y2), y: Math.min(rect.y1, rect.y2),
w: Math.abs(rect.x2 - rect.x1), w: Math.abs(rect.x2 - rect.x1),
h: Math.abs(rect.y2 - rect.y1), h: Math.abs(rect.y2 - rect.y1),
color: rect.color color: rect.color,
fill: rect.fill
}; };
} }
function resizeAndSetupCanvas() { function resizeAndSetupCanvas() {
const dpr = window.devicePixelRatio || 1; dpr = window.devicePixelRatio || 1;
const rect = canvasEl.getBoundingClientRect(); const rect = canvasEl.getBoundingClientRect();
canvasEl.width = rect.width * dpr; canvasEl.width = rect.width * dpr;
@ -140,32 +145,46 @@
} }
function drawRect(rect) { function drawRect(rect) {
ctx.save();
ctx.strokeStyle = rect.color; ctx.strokeStyle = rect.color;
ctx.strokeRect(rect.x, rect.y, rect.w, rect.h); ctx.strokeRect(rect.x, rect.y, rect.w, rect.h);
if (rect.fill) { if (rect.fill) {
ctx.globalAlpha = 0.15; ctx.globalAlpha = 0.15;
ctx.fillStyle = rect.color; ctx.fillStyle = rect.color;
ctx.fillRect(rect.x, rect.y, rect.w, rect.h); ctx.fillRect(rect.x, rect.y, rect.w, rect.h);
ctx.globalAlpha = 1; ctx.globalAlpha = 1;
} }
ctx.restore();
} }
function clearCanvas() { function clearCanvas() {
ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, canvasEl.width / dpr, canvasEl.height / dpr);
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
const dpr = window.devicePixelRatio || 1;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
} }
window.setColor = function setColor() { colorEl.addEventListener('input', () => {
selectedColor = document.getElementById('color').value; selectedColor = document.getElementById('color').value;
} const circle = dotSVGEl.querySelector('circle');
if (circle) {
circle.setAttribute('fill', selectedColor);
}
});
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'z') { const key = e.key.toLowerCase();
if ((e.ctrlKey || e.metaKey) && key === 'z') {
e.preventDefault();
rects.pop(); rects.pop();
redrawAll(); redrawAll();
} }
if (key === 'escape' && currentRect) {
currentRect = null;
redrawAll();
}
}); });
gridEl.addEventListener('mousemove', (e) => { gridEl.addEventListener('mousemove', (e) => {
@ -216,11 +235,12 @@
y1: snapY, y1: snapY,
x2: snapX, x2: snapX,
y2: snapY, y2: snapY,
color: selectedColor color: selectedColor,
fill: document.getElementById('filled').checked
}; };
}); });
gridEl.addEventListener('mouseup', (e) => { window.addEventListener('mouseup', (e) => {
if (!currentRect) return; if (!currentRect) return;
const {ix, iy, x: snapX, y: snapY } = snapToGrid(e.clientX, e.clientY); const {ix, iy, x: snapX, y: snapY } = snapToGrid(e.clientX, e.clientY);