diff options
| author | dax <me@dax.ist> | 2026-08-04 10:09:19 +0000 |
|---|---|---|
| committer | dax <me@dax.ist> | 2026-08-04 10:09:19 +0000 |
| commit | a35909c6b15402174f364be372ad2dad83a39441 (patch) | |
| tree | fe3cc3e284acc7c6d323a1f642667f6fbd128154 /public/app.js | |
| parent | d3d96b58bed27dc11124bdb27310524a84e1583f (diff) | |
pathways: chevrons float fully off the tile and only for the current tile; hover zone covers them; ghost hide is debounced
- chevrons sit entirely in the gap outside the tile (never half-on the photo)
and are drawn only for the current tile, killing the mosaic clutter
- nav zones extend 44px outward so the whole chevron is inside the hit area;
previously the chevron straddled the edge and its outer half was outside the
zone, so mousing across it flipped the ghost on/off (the flicker)
- ghost hide is debounced 80ms and cancelled on the next hover target, so
crossing the gap between a zone and its dropzone no longer blinks the ghost
- setCurrent re-renders navs so chevrons track the current tile
Diffstat (limited to 'public/app.js')
| -rw-r--r-- | public/app.js | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/public/app.js b/public/app.js index 978cdf7..35d9898 100644 --- a/public/app.js +++ b/public/app.js @@ -194,6 +194,7 @@ function setCurrent(id, rect) { if (!userZoomed) scale = worldScale() center() getExits(id) + refreshAllNav() } function renderNav(id) { @@ -202,6 +203,7 @@ function renderNav(id) { t.el.querySelectorAll(".nav").forEach((n) => n.remove()) const exits = exitsCache.get(id) if (!exits) return + const isCurrent = cur && cur.id === id const nav = document.createElement("div") nav.className = "nav" for (const ex of exits) { @@ -210,15 +212,16 @@ function renderNav(id) { zone.addEventListener("pointermove", () => { if (adjacentTile(id, ex.dir)) return zone.classList.add("preview") + cancelHideGhost() showGhost(choosePlacement(id, ex.dir)) }) zone.addEventListener("pointerleave", () => { zone.classList.remove("preview") - hideGhost() + scheduleHideGhost() }) zone.addEventListener("pointerdown", (e) => e.stopPropagation()) zone.addEventListener("click", (e) => { e.stopPropagation(); traverseFrom(id, ex.dir) }) - if (!adjacentTile(id, ex.dir)) { + if (!adjacentTile(id, ex.dir) && isCurrent) { const chev = document.createElement("div") chev.className = "nav-chev" chev.innerHTML = ARROW_SVG[ex.dir] @@ -366,6 +369,15 @@ function hideGhost() { ghostKey = "" } +let hideGhostTimer = 0 +function scheduleHideGhost() { + clearTimeout(hideGhostTimer) + hideGhostTimer = setTimeout(hideGhost, 80) +} +function cancelHideGhost() { + clearTimeout(hideGhostTimer) +} + const zones = new Map() function clearTileZones(id) { const zs = zones.get(id) @@ -393,8 +405,8 @@ function renderTileZones(id) { z.style.top = p.rect.y + "px" z.style.width = p.rect.w + "px" z.style.height = p.rect.h + "px" - z.addEventListener("pointermove", () => showGhost(p)) - z.addEventListener("pointerleave", () => hideGhost()) + z.addEventListener("pointermove", () => { cancelHideGhost(); showGhost(p) }) + z.addEventListener("pointerleave", () => scheduleHideGhost()) z.addEventListener("pointerdown", (e) => e.stopPropagation()) z.addEventListener("click", () => traverseFrom(id, dir)) world.appendChild(z) |
