Conjugate scaling operation by translating to the origin
https://stackoverflow.com/questions/38446666/scaling-around-a-specific-point-in-2d-coordinate-system
This commit is contained in:
parent
f0b5c1a511
commit
99d137cc09
4
package-lock.json
generated
4
package-lock.json
generated
@ -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",
|
||||
|
@ -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": [
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user