Initial commit
This commit is contained in:
77
src/modules/pan.js
Normal file
77
src/modules/pan.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const minDistanceThreshold = 5;
|
||||
|
||||
function distanceBetween({ x: x1, y: y1 }, { x: x2, y: y2 }) {
|
||||
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
||||
}
|
||||
|
||||
function minDistanceThresholdIsMet(startPt, endPt) {
|
||||
return distanceBetween(startPt, endPt) >= minDistanceThreshold;
|
||||
}
|
||||
|
||||
function getPositionChangeInLocalCoords(svg, startPt, endPt) {
|
||||
const matrix = svg.getScreenCTM().inverse(),
|
||||
localStartPt = startPt.matrixTransform(matrix),
|
||||
localEndPt = endPt.matrixTransform(matrix);
|
||||
|
||||
return {
|
||||
x: localStartPt.x - localEndPt.x,
|
||||
y: localStartPt.y - localEndPt.y
|
||||
};
|
||||
}
|
||||
|
||||
function stopEventPropagationToChildren(svg, type) {
|
||||
svg.addEventListener(type, e => e.stopPropagation(), { capture: true, once: true });
|
||||
}
|
||||
|
||||
function setToCurrentPointerCoords(point, e) {
|
||||
point.x = e.clientX;
|
||||
point.y = e.clientY;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
function getPanCoords(svg, startPt, movePt, initialPos) {
|
||||
const posChange = getPositionChangeInLocalCoords(svg, startPt, movePt);
|
||||
|
||||
return {
|
||||
x: initialPos.x + posChange.x,
|
||||
y: initialPos.y + posChange.y
|
||||
};
|
||||
}
|
||||
|
||||
function setViewBoxPosition(svg, { x, y }) {
|
||||
const { width, height } = svg.viewBox.baseVal;
|
||||
|
||||
svg.setAttributeNS(null, 'viewBox', `${x} ${y} ${width} ${height}`);
|
||||
}
|
||||
|
||||
export default function (svg, e) {
|
||||
const { x, y } = svg.viewBox.baseVal,
|
||||
startPt = setToCurrentPointerCoords(new DOMPoint(), e),
|
||||
movePt = new DOMPoint();
|
||||
|
||||
let isPanning = false;
|
||||
|
||||
function pointerMove(e) {
|
||||
setToCurrentPointerCoords(movePt, e);
|
||||
|
||||
if (!isPanning && minDistanceThresholdIsMet(startPt, movePt)) {
|
||||
isPanning = true;
|
||||
e.target.setPointerCapture(e.pointerId);
|
||||
setToCurrentPointerCoords(startPt, e);
|
||||
stopEventPropagationToChildren(svg, 'click');
|
||||
}
|
||||
|
||||
if (isPanning) {
|
||||
setViewBoxPosition(svg, getPanCoords(svg, startPt, movePt, { x, y }));
|
||||
}
|
||||
}
|
||||
|
||||
svg.addEventListener('pointermove', pointerMove);
|
||||
|
||||
svg.addEventListener(
|
||||
'pointerup',
|
||||
() => svg.removeEventListener('pointermove', pointerMove),
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
57
src/modules/zoom.js
Normal file
57
src/modules/zoom.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const zoomStepRatio = 0.25,
|
||||
positive = 1,
|
||||
negative = -1;
|
||||
|
||||
function toLocalCoords(svg, x, y) {
|
||||
const clientP = new DOMPoint(x, y);
|
||||
|
||||
return clientP.matrixTransform(svg.getScreenCTM().inverse());
|
||||
}
|
||||
|
||||
function zoomIn(deltaY) {
|
||||
return deltaY < 0;
|
||||
}
|
||||
|
||||
function calcSizeChangeAmounts(width, height) {
|
||||
return {
|
||||
width: width * zoomStepRatio,
|
||||
height: height * zoomStepRatio
|
||||
};
|
||||
}
|
||||
|
||||
function calcValChangeRatios(focusPoint, x, y, width, height) {
|
||||
return {
|
||||
x: (focusPoint.x - x) / width,
|
||||
y: (focusPoint.y - y) / height,
|
||||
width: (width + x - focusPoint.x) / width,
|
||||
height: (height + y - focusPoint.y) / height
|
||||
};
|
||||
}
|
||||
|
||||
function calcValChangeAmounts(focusPoint, x, y, width, height) {
|
||||
const changeAmount = calcSizeChangeAmounts(width, height),
|
||||
valChangeRatio = calcValChangeRatios(focusPoint, x, y, width, height);
|
||||
|
||||
return {
|
||||
x: valChangeRatio.x * changeAmount.width,
|
||||
y: valChangeRatio.y * changeAmount.height,
|
||||
width: valChangeRatio.width * changeAmount.width,
|
||||
height: valChangeRatio.height * changeAmount.height
|
||||
};
|
||||
}
|
||||
|
||||
export default function (svg, e) {
|
||||
const pointerPosition = toLocalCoords(svg, e.clientX, e.clientY),
|
||||
sign = zoomIn(e.deltaY) ? positive : negative,
|
||||
{ x, y, width, height } = svg.viewBox.baseVal,
|
||||
changeAmount = calcValChangeAmounts(pointerPosition, x, y, width, height),
|
||||
|
||||
attr = {
|
||||
x: x + sign * changeAmount.x,
|
||||
y: y + sign * changeAmount.y,
|
||||
width: width + sign * (-changeAmount.x - changeAmount.width),
|
||||
height: height + sign * (-changeAmount.y - changeAmount.height)
|
||||
};
|
||||
|
||||
return `${attr.x} ${attr.y} ${attr.width} ${attr.height}`;
|
||||
}
|
||||
Reference in New Issue
Block a user