From 829c403bdd9a32d3ebbff9b4e980b4952cdfb5e7 Mon Sep 17 00:00:00 2001 From: Timo Weike Date: Sun, 29 Nov 2020 12:56:19 +0100 Subject: [PATCH 1/7] Added more cancle functions --- index.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/index.js b/index.js index 2833d45..8cc9988 100644 --- a/index.js +++ b/index.js @@ -874,6 +874,12 @@ function createPanZoom(domElement, options) { return zoomByRatio(clientX, clientY, scaleMultiplier); } + function cancelAllAnimations() { + cancelZoomAnimation(); + cancelMoveByAnimation(); + cancelSmoothScroll(); + } + function cancelZoomAnimation() { if (zoomToAnimation) { zoomToAnimation.cancel(); @@ -881,6 +887,17 @@ function createPanZoom(domElement, options) { } } + function cancelSmoothScroll() { + smoothScroll.cancel(); + } + + function cancelMoveByAnimation() { + if (moveByAnimation) { + moveByAnimation.cancel(); + moveByAnimation = null; + } + } + function getScaleMultiplier(delta) { var sign = Math.sign(delta); var deltaAdjustedSpeed = Math.min(0.25, Math.abs(speed * delta / 128)); From d2711194f7940ba9067259591ebafe52fa919ee9 Mon Sep 17 00:00:00 2001 From: Timo Weike Date: Sun, 29 Nov 2020 13:02:19 +0100 Subject: [PATCH 2/7] Use mew cancel functions --- index.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 8cc9988..548a1a9 100644 --- a/index.js +++ b/index.js @@ -168,6 +168,7 @@ function createPanZoom(domElement, options) { } function showRectangle(rect) { + cancelAllAnimations(); // TODO: this duplicates autocenter. I think autocenter should go. var clientRect = owner.getBoundingClientRect(); var size = transformToScreen(clientRect.width, clientRect.height); @@ -449,7 +450,7 @@ function createPanZoom(domElement, options) { return moveBy(dx, dy); } - if (moveByAnimation) moveByAnimation.cancel(); + cancelAllAnimations(); var from = { x: 0, y: 0 }; var to = { x: dx, y: dy }; @@ -467,7 +468,7 @@ function createPanZoom(domElement, options) { } function scroll(x, y) { - cancelZoomAnimation(); + cancelAllAnimations(); moveTo(x, y); } @@ -500,7 +501,7 @@ function createPanZoom(domElement, options) { frameAnimation = 0; } - smoothScroll.cancel(); + cancelAllAnimations(); releaseDocumentMouse(); releaseTouches(); @@ -624,7 +625,7 @@ function createPanZoom(domElement, options) { mouseX = point.x; mouseY = point.y; - smoothScroll.cancel(); + cancelAllAnimations(); startTouchListenerIfNeeded(); } @@ -745,7 +746,7 @@ function createPanZoom(domElement, options) { (e.button === 1 && window.event !== null) || e.button === 0; if (!isLeftButton) return; - smoothScroll.cancel(); + cancelAllAnimations(); var offset = getOffsetXY(e); var point = transformToScreen(offset.x, offset.y); @@ -803,7 +804,7 @@ function createPanZoom(domElement, options) { // if client does not want to handle this event - just ignore the call if (beforeWheel(e)) return; - smoothScroll.cancel(); + cancelAllAnimations(); var delta = e.deltaY; if (e.deltaMode > 0) delta *= 100; @@ -834,8 +835,7 @@ function createPanZoom(domElement, options) { var from = { scale: fromValue }; var to = { scale: scaleMultiplier * fromValue }; - smoothScroll.cancel(); - cancelZoomAnimation(); + cancelAllAnimations(); zoomToAnimation = animate(from, to, { step: function (v) { @@ -850,8 +850,7 @@ function createPanZoom(domElement, options) { var from = { scale: fromValue }; var to = { scale: toScaleValue }; - smoothScroll.cancel(); - cancelZoomAnimation(); + cancelAllAnimations(); zoomToAnimation = animate(from, to, { step: function (v) { @@ -869,8 +868,7 @@ function createPanZoom(domElement, options) { } function publicZoomTo(clientX, clientY, scaleMultiplier) { - smoothScroll.cancel(); - cancelZoomAnimation(); + cancelAllAnimations(); return zoomByRatio(clientX, clientY, scaleMultiplier); } From 02ad3ce299b2cda23be7d02dc9d3adf2efab9700 Mon Sep 17 00:00:00 2001 From: Timo Weike Date: Sun, 29 Nov 2020 13:06:42 +0100 Subject: [PATCH 3/7] Add helper functions --- index.js | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/index.js b/index.js index 548a1a9..8f91270 100644 --- a/index.js +++ b/index.js @@ -187,6 +187,45 @@ function createPanZoom(domElement, options) { transform.scale = scale; } + function internalShowRectangle(rect) { + var newTransform = clientRectToTransform(rect); + + setTransform(newTransform); + } + + + function clientRectToTransform(rect) { + // TODO: this duplicates autocenter. I think autocenter should go. + var clientRect = owner.getBoundingClientRect(); + var size = transformToScreen(clientRect.width, clientRect.height); + + var rectWidth = rect.right - rect.left; + var rectHeight = rect.bottom - rect.top; + if (!Number.isFinite(rectWidth) || !Number.isFinite(rectHeight)) { + throw new Error('Invalid rectangle'); + } + + var dw = size.x / rectWidth; + var dh = size.y / rectHeight; + var scale = Math.min(dw, dh); + + var newTransform = new Transform(); + newTransform.x = -(rect.left + rectWidth / 2) * scale + size.x / 2; + newTransform.y = -(rect.top + rectHeight / 2) * scale + size.y / 2; + newTransform.scale = scale; + + return newTransform; + } + + function setTransform(newTransform) { + transform.x = newTransform.x; + transform.y = newTransform.y; + transform.scale = newTransform.scale; + + triggerEvent('pan'); + triggerEvent('zoom'); + makeDirty(); + } function transformToScreen(x, y) { if (panController.getScreenCTM) { var parentCTM = panController.getScreenCTM(); From 72b63eb15cbb2a5561560c5eb8d2bbfc823491c6 Mon Sep 17 00:00:00 2001 From: Timo Weike Date: Sun, 29 Nov 2020 13:08:07 +0100 Subject: [PATCH 4/7] Use new helper function --- index.js | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 8f91270..a0e8a40 100644 --- a/index.js +++ b/index.js @@ -169,33 +169,16 @@ function createPanZoom(domElement, options) { function showRectangle(rect) { cancelAllAnimations(); - // TODO: this duplicates autocenter. I think autocenter should go. - var clientRect = owner.getBoundingClientRect(); - var size = transformToScreen(clientRect.width, clientRect.height); - - var rectWidth = rect.right - rect.left; - var rectHeight = rect.bottom - rect.top; - if (!Number.isFinite(rectWidth) || !Number.isFinite(rectHeight)) { - throw new Error('Invalid rectangle'); + internalShowRectangle(rect); } - var dw = size.x / rectWidth; - var dh = size.y / rectHeight; - var scale = Math.min(dw, dh); - transform.x = -(rect.left + rectWidth / 2) * scale + size.x / 2; - transform.y = -(rect.top + rectHeight / 2) * scale + size.y / 2; - transform.scale = scale; - } - function internalShowRectangle(rect) { var newTransform = clientRectToTransform(rect); setTransform(newTransform); } - function clientRectToTransform(rect) { - // TODO: this duplicates autocenter. I think autocenter should go. var clientRect = owner.getBoundingClientRect(); var size = transformToScreen(clientRect.width, clientRect.height); @@ -226,6 +209,7 @@ function createPanZoom(domElement, options) { triggerEvent('zoom'); makeDirty(); } + function transformToScreen(x, y) { if (panController.getScreenCTM) { var parentCTM = panController.getScreenCTM(); From 752f551a9f18ec986e09a1c203584eaa27893b01 Mon Sep 17 00:00:00 2001 From: Timo Weike Date: Sun, 29 Nov 2020 13:12:46 +0100 Subject: [PATCH 5/7] Added helper function to calculate an in svg-coord rect from the current Transform --- index.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/index.js b/index.js index a0e8a40..5e3ac13 100644 --- a/index.js +++ b/index.js @@ -210,6 +210,24 @@ function createPanZoom(domElement, options) { makeDirty(); } + // should this be made public? + function transformToClientRect(transform) { + var clientRect = owner.getBoundingClientRect(); + var size = transformToScreen(clientRect.width, clientRect.height); + + var w = size.x / transform.scale; + var h = size.y / transform.scale; + var l = transform.x / -transform.scale; + var t = transform.y / -transform.scale; + + return { + top: t, + left: l, + bottom: t + h, + right: l + w, + }; + } + function transformToScreen(x, y) { if (panController.getScreenCTM) { var parentCTM = panController.getScreenCTM(); From d67a28f99707445ecb6a6475418e207ec66e19ff Mon Sep 17 00:00:00 2001 From: Timo Weike Date: Sun, 29 Nov 2020 13:32:13 +0100 Subject: [PATCH 6/7] Added a smoothShowRectangle function --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ index.d.ts | 1 + index.js | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d83a878..ef534a3 100644 --- a/README.md +++ b/README.md @@ -405,6 +405,51 @@ To pan in a smooth way use `smoothMoveTo(,)`: panzoom.smoothMoveTo(0, 0); ``` +To pan and zoom to a specific in dom/svg coordinate use: + +``` js +rect = { + top: 10, + left: 20, + bottom: 30, + right: 40, +}; + +panzoom.showRectangle(rect); +``` + +to do this in a smooth fashion do + +``` js +panzoom.smoothShowRectangle(rect); +``` +you can also give a function that gets the current rect and the target rect to determine the duration of the animation, like: + +``` js +panzoom.smoothShowRectangle(rect, (from, to) => { + var distance = Math.sqrt( + Math.pow(from.top - to.top, 2) + + Math.pow(from.right - to.right, 2) + + Math.pow(from.bottom - to.bottom, 2) + + Math.pow(from.left - to.left, 2) + ); + + var exp_diff = Math.exp(distance / 1000); + var sigmoid = (exp_diff * 1000) / (exp_diff + 1); + + return sigmoid; +}) +``` + +Further more: the `smoothShowRectangle` return a promise, so that the caller can act on the completion of the animation. + +``` js +panzoom.smoothShowRectangle(rect) + .then(() => { + console.log("animation complete"); + }); +``` + # license MIT diff --git a/index.d.ts b/index.d.ts index 84f20a5..006e9e0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -66,6 +66,7 @@ declare module "panzoom" { ) => void; getTransform: () => Transform; showRectangle: (rect: ClientRect) => void; + smoothShowRectangle: (rect: ClientRect, duration: (from:ClientRect, to:ClientRect) => number) => Promise; pause: () => void; resume: () => void; isPaused: () => boolean; diff --git a/index.js b/index.js index 5e3ac13..54d5286 100644 --- a/index.js +++ b/index.js @@ -102,6 +102,7 @@ function createPanZoom(domElement, options) { var moveByAnimation; var zoomToAnimation; + var showRectangleAnimation; var multiTouch; var paused = false; @@ -119,6 +120,7 @@ function createPanZoom(domElement, options) { smoothZoom: smoothZoom, smoothZoomAbs: smoothZoomAbs, showRectangle: showRectangle, + smoothShowRectangle: publicSmoothShowRectangle, pause: pause, resume: resume, @@ -170,7 +172,7 @@ function createPanZoom(domElement, options) { function showRectangle(rect) { cancelAllAnimations(); internalShowRectangle(rect); - } + } function internalShowRectangle(rect) { var newTransform = clientRectToTransform(rect); @@ -210,6 +212,37 @@ function createPanZoom(domElement, options) { makeDirty(); } + function publicSmoothShowRectangle(rect, duration = undefined) { + cancelAllAnimations(); + + var to = rect; + // get rect from current transform + var from = transformToClientRect(transform); + + // default duration is 600ms + var dur = 600; + if (typeof duration === 'function') { + // let consumer calculate a duration based on the new and current transform + dur = duration(from, to); + } + + var p = new Promise((resolve, _) => { + showRectangleAnimation = animate(from, to, { + duration: dur, + step: function (nextTransform) { + internalShowRectangle(nextTransform); + }, + done: () => { + triggerZoomEnd(); + triggerPanEnd(); + resolve(true); + } + }); + }) + + return p; + } + // should this be made public? function transformToClientRect(transform) { var clientRect = owner.getBoundingClientRect(); @@ -914,6 +947,7 @@ function createPanZoom(domElement, options) { } function cancelAllAnimations() { + cancelShowRectangleAnimation(); cancelZoomAnimation(); cancelMoveByAnimation(); cancelSmoothScroll(); @@ -937,6 +971,13 @@ function createPanZoom(domElement, options) { } } + function cancelShowRectangleAnimation() { + if (showRectangleAnimation) { + showRectangleAnimation.cancel(); + showRectangleAnimation = null; + } + } + function getScaleMultiplier(delta) { var sign = Math.sign(delta); var deltaAdjustedSpeed = Math.min(0.25, Math.abs(speed * delta / 128)); From 692e5f2766ab0f2726ad7b92f751b63024decc44 Mon Sep 17 00:00:00 2001 From: Timo Weike Date: Sun, 29 Nov 2020 13:58:03 +0100 Subject: [PATCH 7/7] Added promises as return values to the other smooth functions --- index.d.ts | 8 ++--- index.js | 92 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/index.d.ts b/index.d.ts index 006e9e0..ad58404 100644 --- a/index.d.ts +++ b/index.d.ts @@ -50,20 +50,20 @@ declare module "panzoom" { dispose: () => void; moveBy: (dx: number, dy: number, smooth: boolean) => void; moveTo: (x: number, y: number) => void; - smoothMoveTo: (x: number, y: number) => void; - centerOn: (ui: any) => void; + smoothMoveTo: (x: number, y: number) => Promise; + centerOn: (ui: any) => Promise; zoomTo: (clientX: number, clientY: number, scaleMultiplier: number) => void; zoomAbs: (clientX: number, clientY: number, zoomLevel: number) => void; smoothZoom: ( clientX: number, clientY: number, scaleMultiplier: number - ) => void; + ) => Promise; smoothZoomAbs: ( clientX: number, clientY: number, toScaleValue: number - ) => void; + ) => Promise; getTransform: () => Transform; showRectangle: (rect: ClientRect) => void; smoothShowRectangle: (rect: ClientRect, duration: (from:ClientRect, to:ClientRect) => number) => Promise; diff --git a/index.js b/index.js index 54d5286..22a31b3 100644 --- a/index.js +++ b/index.js @@ -512,33 +512,46 @@ function createPanZoom(domElement, options) { var dx = container.width / 2 - cx; var dy = container.height / 2 - cy; - internalMoveBy(dx, dy, true); + return internalMoveBy(dx, dy, true); } function smoothMoveTo(x, y){ - internalMoveBy(x - transform.x, y - transform.y, true); + return internalMoveBy(x - transform.x, y - transform.y, true); } function internalMoveBy(dx, dy, smooth) { - if (!smooth) { - return moveBy(dx, dy); - } - - cancelAllAnimations(); - - var from = { x: 0, y: 0 }; - var to = { x: dx, y: dy }; - var lastX = 0; - var lastY = 0; - - moveByAnimation = animate(from, to, { - step: function (v) { - moveBy(v.x - lastX, v.y - lastY); - - lastX = v.x; - lastY = v.y; + var p = new Promise((resolve, _) => { + cancelAllAnimations(); + + if (!smooth) { + moveBy(dx, dy); + + triggerZoomEnd(); + triggerPanEnd(); + resolve(true); + } else { + var from = { x: 0, y: 0 }; + var to = { x: dx, y: dy }; + var lastX = 0; + var lastY = 0; + + moveByAnimation = animate(from, to, { + step: function (v) { + moveBy(v.x - lastX, v.y - lastY); + + lastX = v.x; + lastY = v.y; + }, + done: () => { + triggerZoomEnd(); + triggerPanEnd(); + resolve(true); + } + }); } - }); + }) + + return p; } function scroll(x, y) { @@ -911,12 +924,20 @@ function createPanZoom(domElement, options) { cancelAllAnimations(); - zoomToAnimation = animate(from, to, { - step: function (v) { - zoomAbs(clientX, clientY, v.scale); - }, - done: triggerZoomEnd - }); + var p = new Promise((resolve, _) => { + zoomToAnimation = animate(from, to, { + step: function (v) { + zoomAbs(clientX, clientY, v.scale); + }, + done: () => { + triggerZoomEnd(); + triggerPanEnd(); + resolve(true); + } + }); + }) + + return p; } function smoothZoomAbs(clientX, clientY, toScaleValue) { @@ -926,11 +947,20 @@ function createPanZoom(domElement, options) { cancelAllAnimations(); - zoomToAnimation = animate(from, to, { - step: function (v) { - zoomAbs(clientX, clientY, v.scale); - } - }); + var p = new Promise((resolve, _) => { + zoomToAnimation = animate(from, to, { + step: function (v) { + zoomAbs(clientX, clientY, v.scale); + }, + done: () => { + triggerZoomEnd(); + triggerPanEnd(); + resolve(true); + } + }); + }) + + return p; } function getTransformOriginOffset() {