aboutsummaryrefslogtreecommitdiff
path: root/public/app.js
diff options
context:
space:
mode:
authordax <me@dax.ist>2026-08-04 08:31:33 +0000
committerdax <me@dax.ist>2026-08-04 08:31:33 +0000
commit3b57a64799205f3b8c3ef2a4b74e1a967d36d1e8 (patch)
tree500e54f5b180df87cc57e7c4b810f993dd4e7865 /public/app.js
parent861ccce517f8c879b7c4f3c162ccced856486205 (diff)
pathways: scroll pans / pinch zooms; ghost preview on hover of destination space or arrow (not the zone band); invisible dropzones for current tile
Diffstat (limited to 'public/app.js')
-rw-r--r--public/app.js52
1 files changed, 43 insertions, 9 deletions
diff --git a/public/app.js b/public/app.js
index 9f27b87..aa2eef3 100644
--- a/public/app.js
+++ b/public/app.js
@@ -191,6 +191,7 @@ function setCurrent(id, rect) {
tiles.get(id).el.classList.add("current")
if (!userZoomed) scale = worldScale()
center()
+ renderDropzones(id)
getExits(id)
}
@@ -211,8 +212,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())
+ b.addEventListener("pointerenter", () => showGhost(choosePlacement(id, ex.dir)))
+ b.addEventListener("pointerleave", () => hideGhost())
zone.appendChild(b)
nav.appendChild(zone)
}
@@ -232,6 +233,7 @@ async function getExits(id) {
const exits = data.exits || []
exitsCache.set(id, exits)
renderNav(id)
+ if (cur && cur.id === id) renderDropzones(id)
return exits
} catch {
return null
@@ -309,6 +311,34 @@ function hideGhost() {
}
}
+let dropzones = []
+function clearDropzones() {
+ for (const z of dropzones) world.removeChild(z)
+ dropzones = []
+}
+
+function renderDropzones(id) {
+ clearDropzones()
+ const t = tiles.get(id)
+ if (!t) return
+ for (const dir of ["N", "E", "S", "W"]) {
+ const p = choosePlacement(id, dir)
+ if (!p) continue
+ const z = document.createElement("div")
+ z.className = "dropzone"
+ z.style.left = p.rect.x + "px"
+ z.style.top = p.rect.y + "px"
+ z.style.width = p.rect.w + "px"
+ z.style.height = p.rect.h + "px"
+ z.addEventListener("pointerenter", () => showGhost(p))
+ z.addEventListener("pointerleave", () => hideGhost())
+ z.addEventListener("pointerdown", (e) => e.stopPropagation())
+ z.addEventListener("click", () => moveFrom(id, dir))
+ world.appendChild(z)
+ dropzones.push(z)
+ }
+}
+
stage.addEventListener("pointerover", (e) => {
const tileEl = e.target.closest(".tile")
if (tileEl) getExits(Number(tileEl.dataset.id))
@@ -350,15 +380,19 @@ stage.addEventListener("pointerup", (e) => {
let zoomTimer = 0
stage.addEventListener("wheel", (e) => {
e.preventDefault()
- const rect = stage.getBoundingClientRect()
- const mx = e.clientX - rect.left
- const my = e.clientY - rect.top
+ let dx = e.deltaX
let dy = e.deltaY
- if (e.deltaMode === 1) dy *= 16
- else if (e.deltaMode === 2) dy *= window.innerHeight
- const factor = Math.exp(-dy * (e.ctrlKey ? 0.012 : 0.0018))
+ if (e.deltaMode === 1) { dx *= 16; dy *= 16 }
+ else if (e.deltaMode === 2) { dx *= window.innerWidth; dy *= window.innerHeight }
world.style.transition = "none"
- zoomAt(mx, my, factor)
+ if (e.ctrlKey) {
+ const rect = stage.getBoundingClientRect()
+ zoomAt(e.clientX - rect.left, e.clientY - rect.top, Math.exp(-dy * 0.01))
+ } else {
+ panX -= dx
+ panY -= dy
+ applyPan()
+ }
clearTimeout(zoomTimer)
zoomTimer = setTimeout(() => { world.style.transition = "" }, 120)
}, { passive: false })