feat: Support touch pan & zoom

This commit is contained in:
Sidharth Vinod
2025-04-13 09:51:39 +05:30
parent a5df32931c
commit 6c8402948a
4 changed files with 65 additions and 0 deletions
+4
View File
@@ -16,7 +16,11 @@
"mindmap", "mindmap",
"Pageview", "Pageview",
"pako", "pako",
"panmove",
"panstart",
"panzoom", "panzoom",
"pinchmove",
"pinchstart",
"pzoom", "pzoom",
"roughjs", "roughjs",
"Serde", "Serde",
+2
View File
@@ -30,6 +30,7 @@
"@sveltejs/adapter-static": "3.0.8", "@sveltejs/adapter-static": "3.0.8",
"@sveltejs/kit": "2.20.3", "@sveltejs/kit": "2.20.3",
"@sveltejs/vite-plugin-svelte": "^4.0.4", "@sveltejs/vite-plugin-svelte": "^4.0.4",
"@types/hammerjs": "^2.0.46",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^22.14.1", "@types/node": "^22.14.1",
"@types/pako": "2.0.3", "@types/pako": "2.0.3",
@@ -90,6 +91,7 @@
"@mermaid-js/mermaid-zenuml": "^0.2.0", "@mermaid-js/mermaid-zenuml": "^0.2.0",
"codemirror": "^6.0.1", "codemirror": "^6.0.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"hammerjs": "^2.0.8",
"js-base64": "3.7.7", "js-base64": "3.7.7",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mermaid": "^11.6.0", "mermaid": "^11.6.0",
+17
View File
@@ -35,6 +35,9 @@ importers:
dayjs: dayjs:
specifier: ^1.11.13 specifier: ^1.11.13
version: 1.11.13 version: 1.11.13
hammerjs:
specifier: ^2.0.8
version: 2.0.8
js-base64: js-base64:
specifier: 3.7.7 specifier: 3.7.7
version: 3.7.7 version: 3.7.7
@@ -90,6 +93,9 @@ importers:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: ^4.0.4 specifier: ^4.0.4
version: 4.0.4(svelte@5.26.1)(vite@5.4.16(@types/node@22.14.1)) version: 4.0.4(svelte@5.26.1)(vite@5.4.16(@types/node@22.14.1))
'@types/hammerjs':
specifier: ^2.0.46
version: 2.0.46
'@types/lodash-es': '@types/lodash-es':
specifier: ^4.17.12 specifier: ^4.17.12
version: 4.17.12 version: 4.17.12
@@ -907,6 +913,9 @@ packages:
'@types/geojson@7946.0.16': '@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
'@types/hammerjs@2.0.46':
resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==}
'@types/istanbul-lib-coverage@2.0.6': '@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
@@ -2070,6 +2079,10 @@ packages:
hachure-fill@0.5.2: hachure-fill@0.5.2:
resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
hammerjs@2.0.8:
resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
engines: {node: '>=0.8.0'}
has-flag@4.0.0: has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -4493,6 +4506,8 @@ snapshots:
'@types/geojson@7946.0.16': {} '@types/geojson@7946.0.16': {}
'@types/hammerjs@2.0.46': {}
'@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-coverage@2.0.6': {}
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
@@ -5891,6 +5906,8 @@ snapshots:
hachure-fill@0.5.2: {} hachure-fill@0.5.2: {}
hammerjs@2.0.8: {}
has-flag@4.0.0: {} has-flag@4.0.0: {}
has-symbols@1.1.0: {} has-symbols@1.1.0: {}
+42
View File
@@ -1,4 +1,5 @@
import type { State } from '$/types'; import type { State } from '$/types';
import Hammer from 'hammerjs';
import type { Point } from 'mermaid/dist/types.js'; import type { Point } from 'mermaid/dist/types.js';
import panzoom from 'svg-pan-zoom'; import panzoom from 'svg-pan-zoom';
type PanZoom = typeof panzoom; type PanZoom = typeof panzoom;
@@ -24,9 +25,50 @@ export class PanZoomState {
} }
public updateElement(diagramView: SVGElement, { pan, zoom }: Pick<State, 'pan' | 'zoom'>) { public updateElement(diagramView: SVGElement, { pan, zoom }: Pick<State, 'pan' | 'zoom'>) {
this.pzoom?.destroy();
let hammer: HammerManager | undefined;
this.pzoom = panzoom(diagramView, { this.pzoom = panzoom(diagramView, {
center: true, center: true,
controlIconsEnabled: false, controlIconsEnabled: false,
customEventsHandler: {
haltEventListeners: ['touchstart', 'touchend', 'touchmove', 'touchleave', 'touchcancel'],
init: function (options) {
const instance = options.instance;
let initialScale = 1;
let pannedX = 0;
let pannedY = 0;
hammer = new Hammer(options.svgElement);
hammer.get('pinch').set({ enable: true });
hammer.on('panstart panmove', function (event) {
if (event.type === 'panstart') {
pannedX = 0;
pannedY = 0;
}
instance.panBy({ x: event.deltaX - pannedX, y: event.deltaY - pannedY });
pannedX = event.deltaX;
pannedY = event.deltaY;
});
hammer.on('pinchstart pinchmove', function (event) {
if (event.type === 'pinchstart') {
initialScale = instance.getZoom();
instance.zoomAtPoint(initialScale * event.scale, {
x: event.center.x,
y: event.center.y
});
}
instance.zoomAtPoint(initialScale * event.scale, {
x: event.center.x,
y: event.center.y
});
});
options.svgElement.addEventListener('touchmove', function (event) {
event.preventDefault();
});
},
destroy: function () {
hammer?.destroy();
}
},
fit: true, fit: true,
maxZoom: 12, maxZoom: 12,
minZoom: 0.2, minZoom: 0.2,