diff options
| author | dax <me@dax.ist> | 2026-08-04 08:23:01 +0000 |
|---|---|---|
| committer | dax <me@dax.ist> | 2026-08-04 08:23:01 +0000 |
| commit | 861ccce517f8c879b7c4f3c162ccced856486205 (patch) | |
| tree | 0d5f4073a0ec52ac6364bb40202a05c97d682400 /public | |
| parent | 61d80e5aca1efcf916ab637623848b3543495ed7 (diff) | |
pathways: hover a direction to preview the next photo as a translucent ghost, click to place it (choosePlacement refactor, ghost cleared on move)
Diffstat (limited to 'public')
| -rw-r--r-- | public/app.css | 5 | ||||
| -rw-r--r-- | public/app.js | 42 | ||||
| -rw-r--r-- | public/index.html | 4 |
3 files changed, 46 insertions, 5 deletions
diff --git a/public/app.css b/public/app.css index ceac2aa..e1f3db0 100644 --- a/public/app.css +++ b/public/app.css @@ -44,6 +44,11 @@ html, body { } .tile img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 6px; } .tile.lock { animation: lock .5s cubic-bezier(.22, 1, .36, 1); } +.tile.ghost { + opacity: .5; + pointer-events: none; + box-shadow: 0 0 0 2px rgba(125, 211, 252, .65); +} @keyframes lock { 0% { transform: scale(.2); opacity: 0; } diff --git a/public/app.js b/public/app.js index 2797347..9f27b87 100644 --- a/public/app.js +++ b/public/app.js @@ -211,6 +211,8 @@ function renderNav(id) { b.innerHTML = ARROW_SVG[ex.dir] b.addEventListener("pointerdown", (e) => e.stopPropagation()) b.addEventListener("click", (e) => { e.stopPropagation(); moveFrom(id, ex.dir) }) + zone.addEventListener("pointerenter", () => showGhost(choosePlacement(id, ex.dir))) + zone.addEventListener("pointerleave", () => hideGhost()) zone.appendChild(b) nav.appendChild(zone) } @@ -240,10 +242,10 @@ async function getExits(id) { return p } -async function moveFrom(id, dir) { +function choosePlacement(id, dir) { const exits = exitsCache.get(id) const ex = exits && exits.find((e) => e.dir === dir) - if (!ex) return + if (!ex) return null const from = tiles.get(id).rect const seen = new Set() const candIds = [] @@ -265,15 +267,48 @@ async function moveFrom(id, dir) { const p = bestPlacement(tileW(info), tileH(info), from, dir) if (p) scored.push({ id: cid, info, rect: p.rect, cost: p.cost }) } - if (!scored.length) return + if (!scored.length) return null scored.sort((a, b) => a.cost - b.cost) const best = scored[0] const next = ex.next && !rectOf.has(ex.next.id) ? scored.find((s) => s.id === ex.next.id) : null const chosen = next && next.cost - best.cost <= FIT_TOLERANCE ? next : best + return { id: chosen.id, info: chosen.info, rect: chosen.rect } +} + +function moveFrom(id, dir) { + const chosen = choosePlacement(id, dir) + if (!chosen) return + hideGhost() addTile(chosen.info, chosen.rect) setCurrent(chosen.id, chosen.rect) } +let ghost = null +function showGhost(p) { + hideGhost() + if (!p) return + const el = document.createElement("div") + el.className = "tile ghost" + el.style.left = p.rect.x + "px" + el.style.top = p.rect.y + "px" + el.style.width = p.rect.w + "px" + el.style.height = p.rect.h + "px" + const img = document.createElement("img") + img.src = p.info.thumb + img.alt = "" + img.loading = "lazy" + el.appendChild(img) + world.appendChild(el) + ghost = el +} + +function hideGhost() { + if (ghost) { + world.removeChild(ghost) + ghost = null + } +} + stage.addEventListener("pointerover", (e) => { const tileEl = e.target.closest(".tile") if (tileEl) getExits(Number(tileEl.dataset.id)) @@ -281,6 +316,7 @@ stage.addEventListener("pointerover", (e) => { stage.addEventListener("pointerdown", (e) => { if (e.target.closest(".nav-btn")) return + hideGhost() drag = { x: e.clientX, y: e.clientY, px: panX, py: panY, moved: false, tile: e.target.closest(".tile") } world.style.transition = "none" stage.setPointerCapture(e.pointerId) diff --git a/public/index.html b/public/index.html index 8aa5ed7..5e31bbf 100644 --- a/public/index.html +++ b/public/index.html @@ -5,7 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="color-scheme" content="dark"> <title>pathways</title> - <link rel="stylesheet" href="/app.css?v=6"> + <link rel="stylesheet" href="/app.css?v=7"> </head> <body> <main id="stage"> @@ -16,6 +16,6 @@ <p>four paths, one per direction. walk them.<br><span class="hint">drag to pan · scroll to zoom · double-click empty space or press 0 to fit</span></p> <button id="start">start</button> </div> - <script src="/app.js?v=6"></script> + <script src="/app.js?v=7"></script> </body> </html> |
