Add function for panning programmatically
This commit is contained in:
parent
3b505f69b4
commit
0e1c2d3919
2
index.js
2
index.js
@ -1,2 +1,2 @@
|
|||||||
export { default as pan } from './src/modules/pan.js';
|
export { default as pan, programmaticPan } from './src/modules/pan.js';
|
||||||
export { default as zoom } from './src/modules/zoom.js';
|
export { default as zoom } from './src/modules/zoom.js';
|
||||||
|
@ -2,11 +2,15 @@ import getComputedTransformMatrix from './utils.js';
|
|||||||
|
|
||||||
const minDistanceThreshold = 5;
|
const minDistanceThreshold = 5;
|
||||||
|
|
||||||
|
function mainButtonPressed(e) {
|
||||||
|
return e.button === 0;
|
||||||
|
}
|
||||||
|
|
||||||
function distanceBetween({ x: x1, y: y1 }, { x: x2, y: y2 }) {
|
function distanceBetween({ x: x1, y: y1 }, { x: x2, y: y2 }) {
|
||||||
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function minDistanceThresholdIsMet(startPt, endPt) {
|
function exceedsMinDistanceThreshhold(startPt, endPt) {
|
||||||
return distanceBetween(startPt, endPt) >= minDistanceThreshold;
|
return distanceBetween(startPt, endPt) >= minDistanceThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,45 +24,68 @@ function getTranslateMatrix(startPt, movePt) {
|
|||||||
return translateMatrix.translate(movePt.x - startPt.x, movePt.y - startPt.y);
|
return translateMatrix.translate(movePt.x - startPt.x, movePt.y - startPt.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (svg, el, e) {
|
function getTransformMatrices(el) {
|
||||||
e.preventDefault();
|
return {
|
||||||
|
computed: getComputedTransformMatrix(el),
|
||||||
|
inverseScreen: el.getScreenCTM().inverse()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const mtx = getComputedTransformMatrix(el),
|
function clientToSvgPt({ clientX, clientY }, { inverseScreen }, pt = new DOMPoint()) {
|
||||||
inverseScreenCTM = el.getScreenCTM().inverse();
|
pt.x = clientX;
|
||||||
|
pt.y = clientY;
|
||||||
|
return pt.matrixTransform(inverseScreen);
|
||||||
|
}
|
||||||
|
|
||||||
let startPt = new DOMPoint(e.clientX, e.clientY),
|
function setPanTransform(el, { computed }, startPt, endPt) {
|
||||||
movePt = new DOMPoint(),
|
el.style.transform = computed.multiply(getTranslateMatrix(startPt, endPt));
|
||||||
isPanning = false;
|
}
|
||||||
|
|
||||||
|
export function programmaticPan(el, from, to) {
|
||||||
|
const matrices = getTransformMatrices(el);
|
||||||
|
const startPt = clientToSvgPt(from, matrices);
|
||||||
|
const endPt = clientToSvgPt(to, matrices);
|
||||||
|
|
||||||
|
el.style.transition = 'transform 0.5s';
|
||||||
|
setPanTransform(el, matrices, startPt, endPt);
|
||||||
|
el.addEventListener('transitionend', () => el.style.transition = '', { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (el) {
|
||||||
|
let matrices, startPt, movePt, isPanning;
|
||||||
|
|
||||||
function pointerMove(e) {
|
function pointerMove(e) {
|
||||||
movePt.x = e.clientX;
|
movePt.x = e.clientX;
|
||||||
movePt.y = e.clientY;
|
movePt.y = e.clientY;
|
||||||
|
|
||||||
if (!isPanning && minDistanceThresholdIsMet(startPt, movePt)) {
|
if (!isPanning && exceedsMinDistanceThreshhold(startPt, movePt)) {
|
||||||
isPanning = true;
|
isPanning = true;
|
||||||
e.target.setPointerCapture(e.pointerId);
|
startPt = clientToSvgPt(e, matrices, startPt);
|
||||||
|
|
||||||
startPt.x = e.clientX;
|
|
||||||
startPt.y = e.clientY;
|
|
||||||
startPt = startPt.matrixTransform(inverseScreenCTM);
|
|
||||||
|
|
||||||
stopEventPropagationToChildren(el, 'click');
|
stopEventPropagationToChildren(el, 'click');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPanning) {
|
if (isPanning) {
|
||||||
movePt.x = e.clientX;
|
movePt = clientToSvgPt(e, matrices, movePt);
|
||||||
movePt.y = e.clientY;
|
setPanTransform(el, matrices, startPt, movePt);
|
||||||
movePt = movePt.matrixTransform(inverseScreenCTM);
|
|
||||||
|
|
||||||
el.style.transform = mtx.multiply(getTranslateMatrix(startPt, movePt));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.addEventListener('pointermove', pointerMove);
|
return function(e) {
|
||||||
|
if (!mainButtonPressed(e)) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.target.setPointerCapture(e.pointerId);
|
||||||
|
|
||||||
svg.addEventListener(
|
isPanning = false;
|
||||||
|
matrices = getTransformMatrices(el);
|
||||||
|
startPt = new DOMPoint(e.clientX, e.clientY);
|
||||||
|
movePt = new DOMPoint();
|
||||||
|
|
||||||
|
this.addEventListener('pointermove', pointerMove);
|
||||||
|
|
||||||
|
this.addEventListener(
|
||||||
'pointerup',
|
'pointerup',
|
||||||
() => svg.removeEventListener('pointermove', pointerMove),
|
() => this.removeEventListener('pointermove', pointerMove),
|
||||||
{ once: true }
|
{ once: true }
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,17 @@ function zoomIn(deltaY) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getScale(e, factor) {
|
function getScale(e, factor) {
|
||||||
return zoomIn(e.deltaY) ? 1 + factor : 1 - factor;
|
const outMult = 1 - factor;
|
||||||
|
const inMult = 1 + factor / outMult
|
||||||
|
|
||||||
|
return zoomIn(e.deltaY) ? inMult : outMult;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFocalPointBeforeTransform(el, e, inverseScreenCTM) {
|
function getFocalPointBeforeTransform(el, e, inverseScreenCTM) {
|
||||||
const { x, y, width, height } = el.getBoundingClientRect(),
|
const { x, y, width, height } = el.getBoundingClientRect();
|
||||||
pointer = (new DOMPoint(e.clientX, e.clientY)).matrixTransform(inverseScreenCTM),
|
const pointer = (new DOMPoint(e.clientX, e.clientY)).matrixTransform(inverseScreenCTM);
|
||||||
origin = (new DOMPoint(x, y)).matrixTransform(inverseScreenCTM),
|
const origin = (new DOMPoint(x, y)).matrixTransform(inverseScreenCTM);
|
||||||
terminus = (new DOMPoint(x + width, y + height)).matrixTransform(inverseScreenCTM);
|
const terminus = (new DOMPoint(x + width, y + height)).matrixTransform(inverseScreenCTM);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: pointer.x,
|
x: pointer.x,
|
||||||
@ -25,10 +28,10 @@ function getFocalPointBeforeTransform(el, e, inverseScreenCTM) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFocalPointAfterTransform(el, fpBeforeTrans, inverseScreenCTM) {
|
function getFocalPointAfterTransform(el, fpBeforeTrans, inverseScreenCTM) {
|
||||||
const { x, y, width, height } = el.getBoundingClientRect(),
|
const { x, y, width, height } = el.getBoundingClientRect();
|
||||||
origin = (new DOMPoint(x, y)).matrixTransform(inverseScreenCTM),
|
const origin = (new DOMPoint(x, y)).matrixTransform(inverseScreenCTM);
|
||||||
terminus = (new DOMPoint(x + width, y + height)).matrixTransform(inverseScreenCTM),
|
const terminus = (new DOMPoint(x + width, y + height)).matrixTransform(inverseScreenCTM);
|
||||||
relativeFocalPoint = fpBeforeTrans.relativeToImageSize;
|
const relativeFocalPoint = fpBeforeTrans.relativeToImageSize;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: origin.x + (terminus.x - origin.x) * relativeFocalPoint.x,
|
x: origin.x + (terminus.x - origin.x) * relativeFocalPoint.x,
|
||||||
@ -37,13 +40,13 @@ function getFocalPointAfterTransform(el, fpBeforeTrans, inverseScreenCTM) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTranslateMatrix(el, e, scaleMatrix) {
|
function getTranslateMatrix(el, e, scaleMatrix) {
|
||||||
const inverseScreenCTM = el.getScreenCTM().inverse(),
|
const inverseScreenCTM = el.getScreenCTM().inverse();
|
||||||
fpBeforeTrans = getFocalPointBeforeTransform(el, e, inverseScreenCTM);
|
const fpBeforeTrans = getFocalPointBeforeTransform(el, e, inverseScreenCTM);
|
||||||
|
|
||||||
el.style.transform = scaleMatrix;
|
el.style.transform = scaleMatrix;
|
||||||
|
|
||||||
const fpAfterTrans = getFocalPointAfterTransform(el, fpBeforeTrans, inverseScreenCTM),
|
const fpAfterTrans = getFocalPointAfterTransform(el, fpBeforeTrans, inverseScreenCTM);
|
||||||
translateMatrix = new DOMMatrix();
|
const translateMatrix = new DOMMatrix();
|
||||||
|
|
||||||
return translateMatrix.translate(
|
return translateMatrix.translate(
|
||||||
fpBeforeTrans.x - fpAfterTrans.x,
|
fpBeforeTrans.x - fpAfterTrans.x,
|
||||||
@ -51,12 +54,14 @@ function getTranslateMatrix(el, e, scaleMatrix) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (el, e, factor = 0.1) {
|
export default function (el, factor = 0.1) {
|
||||||
|
return e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const mtx = getComputedTransformMatrix(el),
|
const mtx = getComputedTransformMatrix(el);
|
||||||
scale = getScale(e, factor),
|
const scale = getScale(e, factor);
|
||||||
transMtx = getTranslateMatrix(el, e, mtx.scale(scale));
|
const transMtx = getTranslateMatrix(el, e, mtx.scale(scale));
|
||||||
|
|
||||||
el.style.transform = mtx.multiply(transMtx).scale(scale);
|
el.style.transform = mtx.multiply(transMtx).scale(scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user