aboutsummaryrefslogtreecommitdiff
path: root/public/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/app.js')
-rw-r--r--public/app.js65
1 files changed, 52 insertions, 13 deletions
diff --git a/public/app.js b/public/app.js
index 1c920ae..21b3e49 100644
--- a/public/app.js
+++ b/public/app.js
@@ -11,7 +11,6 @@ const FIT_TOLERANCE = 350
const MIN_ZOOM = 0.05
const MAX_ZOOM = 5
const EPS = 0.5
-const MAX_ZONE_TILES = 24
let U = 300
const tiles = new Map()
@@ -180,16 +179,19 @@ function addTile(info, rect) {
return el
}
-function setCurrent(id, rect) {
+function setCurrent(id, rect, recenter = true) {
const prevId = cur && cur.id
for (const t of tiles.values()) t.el.classList.remove("current")
cur = { id, rect }
tiles.get(id).el.classList.add("current")
- if (!userZoomed) scale = worldScale()
- center()
+ if (recenter) {
+ if (!userZoomed) scale = worldScale()
+ center()
+ }
getExits(id)
if (prevId && prevId !== id) renderNav(prevId)
renderNav(id)
+ refreshPreviews()
}
function renderNav(id) {
@@ -208,6 +210,7 @@ function renderNav(id) {
if (!TOUCH) {
zone.addEventListener("pointermove", () => {
if (adjacentTile(id, ex.dir)) return
+ if (cur && cur.id === id) return
cancelHideGhost()
showGhost(choosePlacement(id, ex.dir))
})
@@ -337,7 +340,6 @@ function traverseFrom(id, dir) {
hideGhost()
addTile(chosen.info, chosen.rect)
setCurrent(chosen.id, chosen.rect)
- refreshAllZones()
}
let ghost = null
@@ -394,7 +396,7 @@ function clearAllZones() {
function renderTileZones(id) {
clearTileZones(id)
- if (TOUCH) return
+ if (!cur || id !== cur.id) return
if (!tiles.has(id) || !exitsCache.has(id)) return
const els = []
for (const dir of ["N", "E", "S", "W"]) {
@@ -407,8 +409,14 @@ 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", () => { cancelHideGhost(); showGhost(p) })
- z.addEventListener("pointerleave", () => scheduleHideGhost())
+ const prev = document.createElement("div")
+ prev.className = "preview"
+ const img = document.createElement("img")
+ img.src = p.info.thumb
+ img.alt = ""
+ img.loading = "lazy"
+ prev.appendChild(img)
+ z.appendChild(prev)
z.addEventListener("pointerdown", (e) => e.stopPropagation())
z.addEventListener("click", () => traverseFrom(id, dir))
world.appendChild(z)
@@ -417,13 +425,41 @@ function renderTileZones(id) {
if (els.length) zones.set(id, els)
}
-function refreshAllZones() {
+function refreshPreviews() {
clearAllZones()
- if (tiles.size > MAX_ZONE_TILES && cur) {
- renderTileZones(cur.id)
- return
+ if (cur) renderTileZones(cur.id)
+}
+
+let lastFollow = 0
+function followCurrent() {
+ if (!tiles.size || !cur) return
+ const cx = (window.innerWidth / 2 - panX) / scale
+ const cy = (window.innerHeight / 2 - panY) / scale
+ const view = {
+ x: cx - window.innerWidth / 2 / scale,
+ y: cy - window.innerHeight / 2 / scale,
+ w: window.innerWidth / scale,
+ h: window.innerHeight / scale,
+ }
+ let best = null
+ let bestD = Infinity
+ for (const t of tiles.values()) {
+ const r = t.rect
+ if (!overlaps(view, r)) continue
+ const px = Math.max(r.x, Math.min(cx, r.x + r.w))
+ const py = Math.max(r.y, Math.min(cy, r.y + r.h))
+ const d = Math.hypot(cx - px, cy - py)
+ if (d < bestD) { bestD = d; best = t }
}
- for (const id of tiles.keys()) if (exitsCache.has(id)) renderTileZones(id)
+ if (!best || best.info.id === cur.id) return
+ setCurrent(best.info.id, best.rect, false)
+}
+
+function maybeFollow() {
+ const now = performance.now()
+ if (now - lastFollow < 200) return
+ lastFollow = now
+ followCurrent()
}
stage.addEventListener("pointerover", (e) => {
@@ -470,6 +506,7 @@ stage.addEventListener("pointermove", (e) => {
panX = drag.px + dx
panY = drag.py + dy
applyPan()
+ maybeFollow()
}
})
@@ -487,6 +524,7 @@ stage.addEventListener("pointerup", (e) => {
drag = null
world.style.transition = ""
if (wasDrag || !tile) {
+ if (wasDrag) followCurrent()
if (!wasDrag && !tile && e.pointerType === "touch") bgTap()
return
}
@@ -528,6 +566,7 @@ stage.addEventListener("wheel", (e) => {
panX -= dx
panY -= dy
applyPan()
+ maybeFollow()
}
clearTimeout(zoomTimer)
zoomTimer = setTimeout(() => { world.style.transition = "" }, 120)