Web Dev Solutions

Catalin Mititiuc

aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCatalin Mititiuc <webdevcat@proton.me>2025-03-17 09:57:58 -0700
committerCatalin Mititiuc <webdevcat@proton.me>2025-03-17 09:58:10 -0700
commit99d137cc0937c0342fc1076eafe609c8aa370087 (patch)
tree6337d50a36ac033169c2217047e23fe786806940
parentf0b5c1a511ba794609178e5f08e7c8f8c2ec1723 (diff)
Conjugate scaling operation by translating to the originHEADv0.3.1master
https://stackoverflow.com/questions/38446666/scaling-around-a-specific-point-in-2d-coordinate-system
-rw-r--r--package-lock.json4
-rw-r--r--package.json2
-rw-r--r--src/modules/pan.js29
-rw-r--r--src/modules/zoom.js61
4 files changed, 36 insertions, 60 deletions
diff --git a/package-lock.json b/package-lock.json
index 91def04..b1fbfc5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "pan-zoom",
- "version": "0.3.0",
+ "version": "0.3.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pan-zoom",
- "version": "0.3.0",
+ "version": "0.3.1",
"license": "ISC",
"devDependencies": {
"esbuild": "^0.20.2",
diff --git a/package.json b/package.json
index 670ddbd..e46850c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pan-zoom",
- "version": "0.3.0",
+ "version": "0.3.1",
"description": "Pan/zoom SVG images in the browser",
"browser": "index.js",
"files": [
diff --git a/src/modules/pan.js b/src/modules/pan.js
index 4e6485c..bf842bd 100644
--- a/src/modules/pan.js
+++ b/src/modules/pan.js
@@ -1,4 +1,4 @@
-import getComputedTransformMatrix from './utils.js';
+import { default as getComputedTransformMatrix } from './utils';
const minDistanceThreshold = 5;
@@ -31,23 +31,26 @@ function getTransformMatrices(el) {
};
}
-function clientToSvgPt({ clientX, clientY }, { inverseScreen }, pt = new DOMPoint()) {
- pt.x = clientX;
- pt.y = clientY;
- return pt.matrixTransform(inverseScreen);
+function clientToSvgPt(e, inverseScreenMtx, pt = new DOMPoint()) {
+ pt.x = e.clientX;
+ pt.y = e.clientY;
+ return pt.matrixTransform(inverseScreenMtx);
}
-function setPanTransform(el, { computed }, startPt, endPt) {
- el.style.transform = computed.multiply(getTranslateMatrix(startPt, endPt));
+function setTransform(el, computedMtx, startPt, endPt) {
+ const translateMtx = getTranslateMatrix(startPt, endPt);
+ const transformMtx = computedMtx.multiply(translateMtx);
+
+ el.style.transform = transformMtx;
}
export function programmaticPan(el, from, to) {
const matrices = getTransformMatrices(el);
- const startPt = clientToSvgPt(from, matrices);
- const endPt = clientToSvgPt(to, matrices);
+ const startPt = clientToSvgPt(from, matrices.inverseScreen);
+ const endPt = clientToSvgPt(to, matrices.inverseScreen);
el.style.transition = 'transform 0.5s';
- setPanTransform(el, matrices, startPt, endPt);
+ setTransform(el, matrices.computed, startPt, endPt);
el.addEventListener('transitionend', () => el.style.transition = '', { once: true });
}
@@ -60,13 +63,13 @@ export default function (el) {
if (!isPanning && exceedsMinDistanceThreshhold(startPt, movePt)) {
isPanning = true;
- startPt = clientToSvgPt(e, matrices, startPt);
+ startPt = clientToSvgPt(e, matrices.inverseScreen, startPt);
stopEventPropagationToChildren(el, 'click');
}
if (isPanning) {
- movePt = clientToSvgPt(e, matrices, movePt);
- setPanTransform(el, matrices, startPt, movePt);
+ movePt = clientToSvgPt(e, matrices.inverseScreen, movePt);
+ setTransform(el, matrices.computed, startPt, movePt);
}
}
diff --git a/src/modules/zoom.js b/src/modules/zoom.js
index 834602b..1455cb4 100644
--- a/src/modules/zoom.js
+++ b/src/modules/zoom.js
@@ -1,67 +1,40 @@
-import getComputedTransformMatrix from './utils.js';
+import { default as getComputedTransformMatrix } from './utils';
function zoomIn(deltaY) {
return deltaY < 0;
}
-function getScale(e, factor) {
+function getScale(deltaY, factor) {
const outMult = 1 - factor;
const inMult = 1 + factor / outMult
- return zoomIn(e.deltaY) ? inMult : outMult;
+ return zoomIn(deltaY) ? inMult : outMult;
}
-function getFocalPointBeforeTransform(el, e, inverseScreenCTM) {
- const { x, y, width, height } = el.getBoundingClientRect();
- const pointer = (new DOMPoint(e.clientX, e.clientY)).matrixTransform(inverseScreenCTM);
- const origin = (new DOMPoint(x, y)).matrixTransform(inverseScreenCTM);
- const terminus = (new DOMPoint(x + width, y + height)).matrixTransform(inverseScreenCTM);
-
- return {
- x: pointer.x,
- y: pointer.y,
- relativeToImageSize: {
- x: (pointer.x - origin.x) / (terminus.x - origin.x),
- y: (pointer.y - origin.y) / (terminus.y - origin.y)
- }
- };
-}
-
-function getFocalPointAfterTransform(el, fpBeforeTrans, inverseScreenCTM) {
- const { x, y, width, height } = el.getBoundingClientRect();
- const origin = (new DOMPoint(x, y)).matrixTransform(inverseScreenCTM);
- const terminus = (new DOMPoint(x + width, y + height)).matrixTransform(inverseScreenCTM);
- const relativeFocalPoint = fpBeforeTrans.relativeToImageSize;
-
- return {
- x: origin.x + (terminus.x - origin.x) * relativeFocalPoint.x,
- y: origin.y + (terminus.y - origin.y) * relativeFocalPoint.y
- };
-}
-
-function getTranslateMatrix(el, e, scaleMatrix) {
+function getTranslateMatrix(el, clientX, clientY) {
const inverseScreenCTM = el.getScreenCTM().inverse();
- const fpBeforeTrans = getFocalPointBeforeTransform(el, e, inverseScreenCTM);
+ const translateMtx = new DOMMatrix();
+ const pointer = new DOMPoint(clientX, clientY)
+ const { x, y } = pointer.matrixTransform(inverseScreenCTM);
- el.style.transform = scaleMatrix;
+ return translateMtx.translate(x, y);
+}
- const fpAfterTrans = getFocalPointAfterTransform(el, fpBeforeTrans, inverseScreenCTM);
- const translateMatrix = new DOMMatrix();
+function setTransform(el, computedMtx, translateMtx, scale) {
+ const transformMtx =
+ computedMtx.multiply(translateMtx).scale(scale).multiply(translateMtx.inverse());
- return translateMatrix.translate(
- fpBeforeTrans.x - fpAfterTrans.x,
- fpBeforeTrans.y - fpAfterTrans.y
- );
+ el.style.transform = transformMtx;
}
export default function (el, factor = 0.1) {
return e => {
e.preventDefault();
- const mtx = getComputedTransformMatrix(el);
- const scale = getScale(e, factor);
- const transMtx = getTranslateMatrix(el, e, mtx.scale(scale));
+ const computedMtx = getComputedTransformMatrix(el);
+ const scale = getScale(e.deltaY, factor);
+ const translateMtx = getTranslateMatrix(el, e.clientX, e.clientY);
- el.style.transform = mtx.multiply(transMtx).scale(scale);
+ setTransform(el, computedMtx, translateMtx, scale);
}
}