Web Dev Solutions

Catalin Mititiuc

import { default as getComputedTransformMatrix } from './utils'; const minDistanceThreshold = 5; function mainButtonPressed(e) { return e.button === 0; } function distanceBetween({ x: x1, y: y1 }, { x: x2, y: y2 }) { return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); } function exceedsMinDistanceThreshhold(startPt, endPt) { return distanceBetween(startPt, endPt) >= minDistanceThreshold; } function stopEventPropagationToChildren(el, type) { el.addEventListener(type, e => e.stopPropagation(), { capture: true, once: true }); } function getTranslateMatrix(startPt, movePt) { const translateMatrix = new DOMMatrix(); return translateMatrix.translate(movePt.x - startPt.x, movePt.y - startPt.y); } function getTransformMatrices(el) { return { computed: getComputedTransformMatrix(el), inverseScreen: el.getScreenCTM().inverse() }; } function clientToSvgPt(e, inverseScreenMtx, pt = new DOMPoint()) { pt.x = e.clientX; pt.y = e.clientY; return pt.matrixTransform(inverseScreenMtx); } 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.inverseScreen); const endPt = clientToSvgPt(to, matrices.inverseScreen); el.style.transition = 'transform 0.5s'; setTransform(el, matrices.computed, startPt, endPt); el.addEventListener('transitionend', () => el.style.transition = '', { once: true }); } export default function (el) { let matrices, startPt, movePt, isPanning; function pointerMove(e) { movePt.x = e.clientX; movePt.y = e.clientY; if (!isPanning && exceedsMinDistanceThreshhold(startPt, movePt)) { isPanning = true; startPt = clientToSvgPt(e, matrices.inverseScreen, startPt); stopEventPropagationToChildren(el, 'click'); } if (isPanning) { movePt = clientToSvgPt(e, matrices.inverseScreen, movePt); setTransform(el, matrices.computed, startPt, movePt); } } return function(e) { if (!mainButtonPressed(e)) return; e.preventDefault(); e.target.setPointerCapture(e.pointerId); isPanning = false; matrices = getTransformMatrices(el); startPt = new DOMPoint(e.clientX, e.clientY); movePt = new DOMPoint(); this.addEventListener('pointermove', pointerMove); this.addEventListener( 'pointerup', () => this.removeEventListener('pointermove', pointerMove), { once: true } ); } }