WEBSITE - WIP - icon shapes - better shape colors + point del button

This commit is contained in:
Alex Sim committed 2026-10-10 23:19:05 +02:00
1 parent 28bc4b3911
commit e5623ed823
2 files changed
+28 -8

No files matched your search

+18 -5
View File
@@ -347,8 +347,7 @@
const crossed = shapes.findIndex((sh) => sh.pts && selfIntersects(sh.pts));
const bad = !!out.error || crossed >= 0;
// The merged icon is filled; every shape and mirrored copy is outlined on top, the selected one solid.
$('se-merged').setAttribute('d', out.d);
// Each shape and mirrored copy is filled and outlined on its own; the selected one is drawn last, on top.
const order = shapes.map((_, i) => i).filter((i) => i !== active).concat(active); // selected shape on top
const strokes = order.flatMap((i) => lines[i].map((d, c) => ({ d, cls: 'se-line' + (i === active ? ' active' : '') + (c ? ' copy' : '') })));
const layer = $('se-lines');
@@ -567,7 +566,8 @@
drag = { kind: 'resize', anchor, d0: dist(anchor, c[g.resize]), orig: shapes[active] };
}
svg.setPointerCapture(e.pointerId);
e.preventDefault();
e.preventDefault(); // this also stops the browser focusing the canvas, so do it here:
svg.focus({ preventScroll: true }); // Delete, arrows and Esc then act on what was just grabbed
render();
});
svg.addEventListener('pointermove', (e) => {
@@ -595,6 +595,13 @@
if (h && +h.dataset.i !== sel) { sel = +h.dataset.i; render(); }
});
// A shape can't go below 3 points: shake the canvas rather than silently ignore the key.
const shake = () => {
const stage = svg.parentElement;
stage.classList.remove('se-shake');
void stage.offsetWidth; // restarts the animation when pressed again mid-shake
stage.classList.add('se-shake');
};
document.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
if ((e.ctrlKey || e.metaKey) && !e.altKey && (key === 'z' || key === 'y')) {
@@ -602,7 +609,13 @@
e.preventDefault();
return (key === 'y' || e.shiftKey ? $('se-redo') : $('se-undo')).click(); // Ctrl+Y, or Ctrl/Cmd+Shift+Z
}
if (!svg.contains(document.activeElement)) return;
// Delete, Backspace and Esc act on the selected point wherever the focus is (say, on its Size slider),
// unless you're typing in a field or a dialog is open. Arrows still need the canvas focused, so elsewhere
// they keep moving sliders and scrolling the page.
const typing = e.target.matches('input:not([type=range]):not([type=checkbox]), textarea, select');
const pointKey = sel >= 0 && mode === 'edit' && ['Delete', 'Backspace', 'Escape'].includes(e.key)
&& !typing && !e.target.closest('.contact-modal-overlay');
if (!pointKey && !svg.contains(document.activeElement)) return;
const step = grid.snap ? grid.step : e.shiftKey ? 5 : 0.5;
const mv = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[e.key];
if (mode === 'move') { // arrows move the whole shape
@@ -616,7 +629,7 @@
pts[sel].x = snap(pts[sel].x + mv[0] * step);
pts[sel].y = snap(pts[sel].y + mv[1] * step);
} else if (e.key === 'Delete' || e.key === 'Backspace') {
if (pts.length <= 3) return;
if (pts.length <= 3) return shake();
checkpoint();
pts.splice(sel, 1);
sel = Math.min(sel, pts.length - 1);
+10 -3
View File
@@ -26,8 +26,13 @@ noindex: true # and out of search engines
.se-grid-line { stroke: var(--md-outline-variant); stroke-width: .15; }
.se-grid-mid { stroke: var(--md-outline); stroke-width: .25; }
.se-bounds { fill: none; stroke: var(--md-outline); stroke-width: .35; stroke-dasharray: 1.5 1; }
.se-merged { fill: var(--md-primary-container); fill-opacity: .85; }
.se-line { fill: none; stroke: var(--md-outline); stroke-width: .35; stroke-dasharray: 1.2 1; }
/* Every shape is filled on its own: the others softly and behind, the selected one stronger and on top. */
.se-stage { --se-fill-active: color-mix(in srgb, var(--md-primary) 50%, transparent);
--se-fill-other: color-mix(in srgb, var(--md-primary-container) 85%, transparent); }
[data-theme="dark"] .se-stage { --se-fill-active: color-mix(in srgb, var(--md-primary-container) 85%, transparent);
--se-fill-other: color-mix(in srgb, var(--md-primary-container) 30%, transparent); }
.se-line { fill: var(--se-fill-other); stroke: var(--md-outline); stroke-width: .35; stroke-dasharray: 1.2 1; }
.se-line.active { fill: var(--se-fill-active); }
.se-line.copy { stroke-dasharray: .4 .8; }
.se-line.active { stroke: var(--md-primary); stroke-width: .5; stroke-dasharray: none; }
.se-line.active.copy { stroke-width: .35; stroke-dasharray: 1.2 1; }
@@ -101,6 +106,9 @@ noindex: true # and out of search engines
/* Canvas overlay (Move mode): fit this shape top right, mirroring bottom left. */
.se-left { min-width: 0; }
.se-stage { position: relative; }
.se-shake { animation: se-shake .35s; }
@keyframes se-shake { 20%, 60% { transform: translateX(-6px); } 40%, 80% { transform: translateX(6px); } }
@media (prefers-reduced-motion: reduce) { .se-shake { animation: none; } }
.se-stage-btn, .se-mirror button { display: inline-flex; align-items: center; justify-content: center; padding: 0;
border-radius: var(--md-shape-full); color: var(--md-primary); cursor: pointer; }
.se-stage-btn { position: absolute; top: 10px; right: 10px; width: 40px; height: 40px;
@@ -159,7 +167,6 @@ noindex: true # and out of search engines
<svg id="se-canvas" viewBox="-20 -20 140 140" tabindex="0" aria-label="Shape editor. In Edit mode drag points to move them and the plus marks to add points; in Move mode drag the shape or its corners.">
<g id="se-grid"></g>
<rect class="se-bounds" x="0" y="0" width="100" height="100"/>
<path id="se-merged" class="se-merged"/>
<g id="se-lines"></g>
<polygon id="se-outline" class="se-outline"/>
<g id="se-adders"></g>