sbuffkin_hexmaker/dev/cal-handle-size-check.html
isaprettycoolguy@protonmail.com 6882a3b504 perf+fix: calibration handle sizing + snapshot-based drag/resize/nudge
Resize handles live inside the scaled bg-image (~4.8×) / grid (~0.35×) layers,
but --duckmage-cal-scale only cancelled the viewport zoom, so image handles
rendered ~5× too large and grid handles too small. updateCalHandleScale() now
sets the var per layer to 1/(zoom × layerScale) so handles stay a constant
on-screen size at any zoom/scale.

Calibration drag/resize/arrow-nudge was paint-bound at ~2fps: moving or scaling
the grid re-rasterises all 3843 clip-path hex cells every frame (a probe showed
~0.05ms JS work vs ~540ms frame gaps). During an active adjustment we now swap
the cells for a single pre-drawn <canvas> colour snapshot (buildCalibrationSnapshot)
— transforming one canvas is a single composite op — and drop icons/labels/the
SVG outline. Mouse drags coalesce moves to one rAF tick and tear down on mouseup;
arrow-key nudges use a bake-on-quiet settle timer (no mouseup to end on).

Adds a dev-gated drag probe (window.DUCKMAGE_PERF) and dev/cal-handle-size-check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:04:12 -04:00

46 lines
1.9 KiB
HTML

<!doctype html>
<html><head><meta charset="utf-8"/><style>
.viewport { position: absolute; top: 100px; left: 100px; transform-origin: 0 0;
transform: scale(var(--vp, 1)); }
.layer { position: absolute; width: 200px; height: 150px; transform-origin: 0 0;
transform: scale(var(--ls, 1)); background: #357; }
/* Mirror of styles.css handle rule (size + counter-scale). */
.handle { position: absolute; width: 18px; height: 18px; background: red;
transform-origin: center; }
.handle.nw { top: -9px; left: -9px; transform: scale(var(--cal, 1)); }
</style></head>
<body>
<div id="root"></div>
<script>
const root = document.getElementById("root");
// OLD behaviour: cal = 1/zoom (ignores layer scale). NEW: cal = 1/(zoom*layerScale).
function build(zoom, layerScale, mode) {
root.replaceChildren();
const vp = document.createElement("div"); vp.className = "viewport";
vp.style.setProperty("--vp", zoom);
const layer = document.createElement("div"); layer.className = "layer";
layer.style.setProperty("--ls", layerScale);
const cal = mode === "new" ? 1 / (zoom * layerScale) : 1 / zoom;
layer.style.setProperty("--cal", cal);
const handle = document.createElement("div"); handle.className = "handle nw";
layer.appendChild(handle); vp.appendChild(layer); root.appendChild(vp);
void document.body.offsetHeight;
return handle.getBoundingClientRect().width; // on-screen px
}
window.__check = () => {
const cases = [
["bg image zoomed out", 0.3, 4.84],
["bg image zoom 1", 1.0, 4.84],
["bg image zoomed in", 2.5, 4.84],
["grid zoomed out", 0.3, 0.35],
["grid zoom 1", 1.0, 0.35],
["grid zoomed in", 2.5, 0.35],
];
return cases.map(([name, z, ls]) => ({
name, zoom: z, layerScale: ls,
oldPx: +build(z, ls, "old").toFixed(1),
newPx: +build(z, ls, "new").toFixed(1),
}));
};
document.title = "ready";
</script></body></html>