index : pan-zoom | |
SVG pan/zoom library. |
aboutsummaryrefslogtreecommitdiff |
diff options
-rw-r--r-- | public/index.html | 30 | ||||
-rw-r--r-- | src/app.js | 25 | ||||
-rw-r--r-- | src/modules/pan.js | 6 | ||||
-rw-r--r-- | src/modules/utils.js | 27 | ||||
-rw-r--r-- | src/modules/zoom.js | 6 |
5 files changed, 73 insertions, 21 deletions
diff --git a/public/index.html b/public/index.html index d97baee..bfcf3ef 100644 --- a/public/index.html +++ b/public/index.html @@ -7,13 +7,29 @@ <link rel="stylesheet" href="assets/css/style.css"> </head> <body> - <h1>Pan & Zoom SVG Element with CSS/JavaScript</h1> - - <p> - Click and drag the image to pan. Use the mouse wheel to zoom in and out. - </p> - - <object type="image/svg+xml" data="assets/images/image.svg"></object> + <!--<object type="image/svg+xml" data="assets/images/image.svg"></object>--> + <svg viewBox="-200 -150 400 300"> + <defs> + <circle id="zoom-marker" cx="0" cy="0" r="30" fill="maroon" /> + <circle id="pan-marker" cx="0" cy="0" r="20" fill="darkgray" /> + </defs> + <g class="pan-zoom"> + <use id="zm-1" href="#zoom-marker" x="-50" y="-50" /> + <use id="zm-2" href="#zoom-marker" x="-50" y="50" /> + <use id="zm-3" href="#zoom-marker" x="50" y="-50" /> + <use id="zm-4" href="#zoom-marker" x="50" y="50" /> + </g> + <g class="pan"> + <use id="pm-1" href="#pan-marker" x="-50" y="-50" /> + <use id="pm-2" href="#pan-marker" x="-50" y="50" /> + <use id="pm-3" href="#pan-marker" x="50" y="-50" /> + <use id="pm-4" href="#pan-marker" x="50" y="50" /> + </g> + <g> + <!--<circle id="pointer" cx="0" cy="0" r="5" fill="red" stroke="maroon"/>--> + <circle cx="0" cy="0" r="1" fill="white" /> + </g> + </svg> <script src="app.js"></script> </body> </html> @@ -1,27 +1,28 @@ import zoom from './modules/zoom'; import pan from './modules/pan'; -const optionalZoomFactor = 0.1, - object = document.querySelector('object'); +const optionalZoomFactor = 0.1 + //, object = document.querySelector('object') + ; // If embedding an SVG using an <object> tag, it's necessary to wait until the // page has loaded before querying its `contentDocument`, otherwise it will be // `null`. window.addEventListener('load', function () { - const svg = object.contentDocument.querySelector('svg'), - targetEl = svg.querySelector('g'), - pointer = svg.querySelector('#pointer'), + const svg = document.querySelector('svg'), + targetEl = svg.querySelector('g.pan-zoom'), + //pointer = svg.querySelector('#pointer'), options = { passive: false }; svg.addEventListener('wheel', zoom(targetEl, optionalZoomFactor), options); svg.addEventListener('pointerdown', pan(targetEl), options); - svg.addEventListener('pointermove', e => { - const pt = new DOMPoint(e.clientX, e.clientY), - svgP = pt.matrixTransform(targetEl.getScreenCTM().inverse()); - - pointer.setAttributeNS(null, 'cx', svgP.x); - pointer.setAttributeNS(null, 'cy', svgP.y); - }); + //svg.addEventListener('pointermove', e => { + // const pt = new DOMPoint(e.clientX, e.clientY), + // svgP = pt.matrixTransform(targetEl.getScreenCTM().inverse()); + // + // pointer.setAttributeNS(null, 'cx', svgP.x); + // pointer.setAttributeNS(null, 'cy', svgP.y); + //}); }); diff --git a/src/modules/pan.js b/src/modules/pan.js index bf842bd..8be3cb9 100644 --- a/src/modules/pan.js +++ b/src/modules/pan.js @@ -1,4 +1,4 @@ -import { default as getComputedTransformMatrix } from './utils'; +import { default as getComputedTransformMatrix, track, getTracked } from './utils'; const minDistanceThreshold = 5; @@ -41,9 +41,13 @@ function setTransform(el, computedMtx, startPt, endPt) { const translateMtx = getTranslateMatrix(startPt, endPt); const transformMtx = computedMtx.multiply(translateMtx); + groups.forEach(([z, p]) => track(z, p, transformMtx)); + el.style.transform = transformMtx; } +const groups = getTracked(document.querySelectorAll('svg g[class] use')); + export function programmaticPan(el, from, to) { const matrices = getTransformMatrices(el); const startPt = clientToSvgPt(from, matrices.inverseScreen); diff --git a/src/modules/utils.js b/src/modules/utils.js index e7f5c55..ae63b74 100644 --- a/src/modules/utils.js +++ b/src/modules/utils.js @@ -1,5 +1,32 @@ const digits = /-?\d+\.?\d*/g; +export function extractNum(string) { + return string.replace(/\D/g,''); +} + +export function getTracked(trackedAndTrackerEls) { + const sorted = [...trackedAndTrackerEls] + .sort((a, b) => a.id < b.id || extractNum(a.id) <= extractNum(b.id)); + + const groups = []; + const els = sorted.slice(); + + while (els.length) { + groups.push([els.pop(), els.pop()]); + } + + return groups; +} + +export function track(targetEl, trackingEl, transformMtx) { + let x = targetEl.getAttributeNS(null, 'x'); + let y = targetEl.getAttributeNS(null, 'y'); + let ptBefore = new DOMPoint(x, y); + let ptAfter = ptBefore.matrixTransform(transformMtx); + trackingEl.setAttributeNS(null, 'x', ptAfter.x); + trackingEl.setAttributeNS(null, 'y', ptAfter.y); +} + export default function getComputedTransformMatrix(el) { const matrixSequence = getComputedStyle(el).transform.match(digits), identityMatrix = ''; diff --git a/src/modules/zoom.js b/src/modules/zoom.js index 1455cb4..69652c0 100644 --- a/src/modules/zoom.js +++ b/src/modules/zoom.js @@ -1,4 +1,4 @@ -import { default as getComputedTransformMatrix } from './utils'; +import { default as getComputedTransformMatrix, extractNum, track, getTracked } from './utils'; function zoomIn(deltaY) { return deltaY < 0; @@ -24,9 +24,13 @@ function setTransform(el, computedMtx, translateMtx, scale) { const transformMtx = computedMtx.multiply(translateMtx).scale(scale).multiply(translateMtx.inverse()); + groups.forEach(([z, p]) => track(z, p, transformMtx)); + el.style.transform = transformMtx; } +const groups = getTracked(document.querySelectorAll('svg g[class] use')); + export default function (el, factor = 0.1) { return e => { e.preventDefault(); |