aboutsummaryrefslogtreecommitdiff
path: root/public/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/app.js')
-rw-r--r--public/app.js47
1 files changed, 46 insertions, 1 deletions
diff --git a/public/app.js b/public/app.js
index 6c69f92..2797347 100644
--- a/public/app.js
+++ b/public/app.js
@@ -8,6 +8,8 @@ const startBtn = $("#start")
const GAP = 10
const FIT_TOLERANCE = 350
+const MIN_ZOOM = 0.05
+const MAX_ZOOM = 5
const ARROW_SVG = {
N: '<svg viewBox="0 0 24 24"><path d="M5 15l7-8 7 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
E: '<svg viewBox="0 0 24 24"><path d="M9 5l8 7-8 7" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
@@ -25,6 +27,7 @@ let cur = null
let panX = 0
let panY = 0
let scale = 1
+let userZoomed = false
let drag = null
function aspect(info) {
@@ -59,6 +62,24 @@ function center() {
applyPan()
}
+function zoomAt(mx, my, factor) {
+ const ns = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, scale * factor))
+ if (ns === scale) return
+ const wx = (mx - panX) / scale
+ const wy = (my - panY) / scale
+ panX = mx - wx * ns
+ panY = my - wy * ns
+ scale = ns
+ userZoomed = true
+ applyPan()
+}
+
+function resetZoom() {
+ userZoomed = false
+ scale = worldScale()
+ center()
+}
+
function overlaps(a, b) {
return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y
}
@@ -168,7 +189,7 @@ function setCurrent(id, rect) {
for (const t of tiles.values()) t.el.classList.remove("current")
cur = { id, rect }
tiles.get(id).el.classList.add("current")
- scale = worldScale()
+ if (!userZoomed) scale = worldScale()
center()
getExits(id)
}
@@ -290,12 +311,36 @@ stage.addEventListener("pointerup", (e) => {
setCurrent(id, tiles.get(id).rect)
})
+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 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))
+ world.style.transition = "none"
+ zoomAt(mx, my, factor)
+ clearTimeout(zoomTimer)
+ zoomTimer = setTimeout(() => { world.style.transition = "" }, 120)
+}, { passive: false })
+
+stage.addEventListener("dblclick", (e) => {
+ if (e.target.closest(".tile")) return
+ resetZoom()
+})
+
addEventListener("keydown", (e) => {
const map = { ArrowUp: "N", ArrowRight: "E", ArrowDown: "S", ArrowLeft: "W" }
const dir = map[e.key]
if (dir) {
e.preventDefault()
if (cur) moveFrom(cur.id, dir)
+ } else if (e.key === "0" || e.key === "f" || e.key === "F") {
+ e.preventDefault()
+ resetZoom()
}
})