panzoom

Extensible, mobile-friendly pan and zoom library for DOM and SVG elements with kinetic scrolling.

Library
npm
v9.4.4
2,005stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
45/100Fair
Development Activity8
Maintenance0
Community72
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
65/100Good
Architecture68
Code Quality55
Innovation82
Learning Curve55

Panzoom is a small, framework-free JavaScript library that adds drag-to-pan and pinch/wheel-to-zoom behavior to any DOM subtree or SVG element. It ships a pluggable controller architecture that transparently handles the differences between CSS transforms on regular DOM elements and matrix transforms on SVG, so the same API works for either without extra configuration.

Beyond basic panning and zooming, it supports kinetic (physics-based momentum) scrolling, configurable bounds so content can’t be dragged out of view, min/max zoom limits, a customizable transform origin, keyboard navigation, and an event system (pan/zoom/panstart/panend/transform) for reacting to user interaction. It can be installed via npm for bundler-based projects or dropped in via a script tag with a declarative pz- attribute API for zero-JavaScript setup.

What You Get

  • A single panzoom(element, options) call that adds full pan/zoom interaction to any DOM or SVG element with zero required configuration
  • Kinetic (momentum-based) scrolling after a drag, with tunable amplitude and time-constant physics
  • Bounds and min/max zoom constraints so users can’t pan or zoom content out of a usable range
  • An event API (on/off/fire) for pan, panstart, panend, zoom, zoomend, and transform events
  • A declarative script-tag attachment mode (query + pz- prefixed attributes) for use without a bundler
  • Hand-maintained TypeScript declarations (index.d.ts) for full autocomplete in TS projects

Common Use Cases

  • Adding pan/zoom to an interactive SVG diagram, map, or graph visualization embedded in a web page
  • Building an image or canvas viewer where users can drag to pan and scroll/pinch to zoom
  • Powering infinite-canvas or whiteboard-style UIs where a scene graph needs to be navigable
  • Adding touch-friendly pan/zoom to a mobile web view without pulling in a full charting or mapping framework

Under The Hood

Architecture The entry point (index.js, ~1,100 lines) implements createPanZoom, which detects the target element’s type via canAttach() checks and delegates DOM-specific vs. SVG-specific transform handling to two small, swappable controller modules (lib/makeDomController.js, lib/makeSvgController.js), each exposing the same narrow interface (getOwner, getBBox, getScreenCTM, applyTransform, initTransform) — a lightweight strategy pattern that keeps the core event/gesture logic identical regardless of target type. Core transform state lives in a small Transform class (lib/transform.js); momentum scrolling is isolated in lib/kinetic.js as an independently start/stop-able physics ticker driven by requestAnimationFrame; and text-selection suppression during drags is injected via lib/makeTextSelectionInterceptor.js. This keeps the module boundaries clean, but mouse, wheel, touch, pinch, and keyboard handling are all centralized as closures inside the single large entry file, making it the piece most exposed to change if the controller interface itself needs to evolve.

Tech Stack Panzoom is dependency-light vanilla JavaScript (CommonJS, ES5-style) with no runtime framework: it pulls in only three small libraries by the same author — wheel (cross-browser wheel-event normalization), amator (tweening for smooth zoom animations), and ngraph.events (the pub/sub backing .on()/.off()/.fire()). Distribution builds run through Browserify into a UMD-style dist/panzoom.js, minified with uglify-js, for direct <script>/CDN use; tests run under tap with jsdom simulating a DOM so no headless browser is required. TypeScript consumers get hand-written ambient types in index.d.ts rather than types generated from source.

Code Quality The test suite (test/panzoom.js, test/kinetic.js) exercises creation, min/max zoom, wheel-driven transform updates, pause/resume, and event firing through tap against a jsdom-simulated DOM, but the configured coverage thresholds are low (30% for branches/lines/statements/functions) and the SVG/DOM controller modules have no dedicated tests of their own. Error handling favors explicit throw new Error(...) guards for invalid usage (e.g. attaching to the root <svg> element) over a broader typed-error pattern. Naming is consistent camelCase throughout, GitHub Actions runs the test suite on push, and ESLint provides basic linting, though there’s no enforced formatter and the runtime JS and its hand-maintained .d.ts types can drift independently.

API Design The public surface is a single default export, createPanZoom(domElement, options), returning an instance with a small, well-named API (moveTo, zoomTo, smoothZoom, getTransform, on/off/fire, pause/resume). Getting started needs exactly one line — panzoom(element) — with every option (bounds, zoomSpeed, minZoom/maxZoom, transformOrigin, filterKey) opt-in and sensibly defaulted. The README documents each option with runnable snippets and live JSFiddle demos, and the script-tag attachment mode gives non-bundler users a zero-JavaScript path in. The main rough edge is that the eventify-based .on() API returns no unsubscribe handle.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search