aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/api/dashboard/dashboard.html52
1 files changed, 48 insertions, 4 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html
index 5ca7409..38a1a5c 100644
--- a/src/api/dashboard/dashboard.html
+++ b/src/api/dashboard/dashboard.html
@@ -152,7 +152,7 @@
</div>
<div class="legend" id="legend"></div>
<div class="legend">
- <span class="hint">drag to pan, wheel to zoom, double-click to reset; drag a node to
+ <span class="hint">drag to pan, wheel or pinch to zoom, double-click to reset; drag a node to
pin it, double-click it to let go</span>
</div>
</div>
@@ -1028,11 +1028,13 @@ function pointer(event) {
const box = graph.getBoundingClientRect();
return { x: event.clientX - box.left, y: event.clientY - box.top };
}
+// How far it zooms: from a quarter to eight times.
+const zoomed = k => Math.min(8, Math.max(.25, k));
// The wheel zooms around the pointer.
graph.addEventListener("wheel", event => {
event.preventDefault();
const at = pointer(event);
- const k = Math.min(8, Math.max(.25, view.k * Math.exp(-event.deltaY * .002)));
+ const k = zoomed(view.k * Math.exp(-event.deltaY * .002));
view.x = at.x - (at.x - view.x) * k / view.k;
view.y = at.y - (at.y - view.y) * k / view.k;
view.k = k;
@@ -1045,7 +1047,47 @@ function pressing(event, move, click) {
press = { from: pointer(event), moved: false, move, click };
graph.setPointerCapture(event.pointerId);
}
+// Fingers on the map, by pointer. Two pinch: the map zooms around the point between
+// them and moves with it. A press (`pressing`) is given up when the second comes.
+const touching = new Map();
+let pinch = null;
+const between = (a, b) => ({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 });
+// From the first two fingers as they are now.
+function pinchFrom() {
+ const [a, b] = touching.values();
+ pinch = { view: { ...view }, at: between(a, b),
+ distance: Math.max(Math.hypot(b.x - a.x, b.y - a.y), 1) };
+}
+function pinched() {
+ const [a, b] = touching.values();
+ const at = between(a, b);
+ const k = zoomed(pinch.view.k * Math.hypot(b.x - a.x, b.y - a.y) / pinch.distance);
+ // What was between the fingers stays between them.
+ view.x = at.x - (pinch.at.x - pinch.view.x) * k / pinch.view.k;
+ view.y = at.y - (pinch.at.y - pinch.view.y) * k / pinch.view.k;
+ view.k = k;
+ moveView();
+}
+// Before the map's own handlers (a node's included): a second finger takes over.
+graph.addEventListener("pointerdown", event => {
+ if (event.pointerType !== "touch") return;
+ touching.set(event.pointerId, pointer(event));
+ graph.setPointerCapture(event.pointerId);
+ if (touching.size < 2) return;
+ press = null;
+ graph.classList.remove("dragging");
+ pinchFrom();
+ event.stopImmediatePropagation();
+}, true);
+// A finger lifted: the others pinch on from where they are, or (one left) do nothing
+// until lifted too.
+function untouched(event) {
+ if (!touching.delete(event.pointerId)) return;
+ if (touching.size >= 2) pinchFrom(); else pinch = null;
+}
graph.addEventListener("pointermove", event => {
+ if (touching.has(event.pointerId)) touching.set(event.pointerId, pointer(event));
+ if (pinch) { pinched(); return; }
if (!press) return;
const at = pointer(event);
const dx = at.x - press.from.x, dy = at.y - press.from.y;
@@ -1054,13 +1096,15 @@ graph.addEventListener("pointermove", event => {
graph.classList.add("dragging");
press.move(dx, dy);
});
-graph.addEventListener("pointerup", () => {
+graph.addEventListener("pointerup", event => {
+ untouched(event);
const click = press && !press.moved ? press.click : null;
press = null;
graph.classList.remove("dragging");
if (click) click();
});
-graph.addEventListener("pointercancel", () => {
+graph.addEventListener("pointercancel", event => {
+ untouched(event);
press = null;
graph.classList.remove("dragging");
});