A lightweight, high-performance panner zoomer container built as a native web component. Zero dependencies, works everywhere, and fundamental to modern web applications.
npm install panner-zoomerA lightweight, high-performance panner zoomer container built as a native web component. Zero dependencies, works everywhere, and fundamental to modern web applications.
- Pure Web Components - No framework required, works with vanilla JS, React, Vue, Angular, etc.
- Accurate Coordinate Transformations - Precise world-to-screen and screen-to-world conversions
- Industry-Standard Zoom - Cursor stays locked to the same point (like Google Maps, Figma)
- Smooth Panning - Native pointer events for responsive interaction
- Programmatic Control - Full API for controlling pan and zoom
- Reactive Attributes - panx, pany, and zoom attributes update in real-time
- Built-in Controls - Optional control panel component with customizable placement
- Comprehensive Tests - Built-in test suite for mathematical correctness
The simplest way to get started - just wrap your content:
``html

`
Add the built-in control panel for zoom in, zoom out, and reset:
`html

`
Control Placement Options:
- ne - Northeast (top-right) - defaultnw
- - Northwest (top-left)se
- - Southeast (bottom-right)sw
- - Southwest (bottom-left)
Make elements draggable within the panner-zoomer space:
`html
Drag me!
`
Control pan and zoom via JavaScript:
`html

`
Set initial pan and zoom using HTML attributes:
`html`

The attributes automatically update as users interact:
`html`
React to pan and zoom changes:
`html`
#### Attributes
| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| panx | number | 0 | Horizontal pan offset (updates automatically) |pany
| | number | 0 | Vertical pan offset (updates automatically) |zoom
| | number | 1 | Zoom level (updates automatically) |
#### Methods
##### setPan(x, y)
Set the pan position.
`javascript`
pz.setPan(100, 50);
Parameters:
- x (number) - Horizontal pan offset in pixelsy
- (number) - Vertical pan offset in pixels
---
##### setZoom(scale)
Set the zoom level.
`javascript`
pz.setZoom(2); // 2x zoom
Parameters:
- scale (number) - Zoom scale (must be between minScale and maxScale)
---
##### zoomToPoint(scale, worldX, worldY)
Zoom to a specific world coordinate, centering it in the viewport.
`javascript`
pz.zoomToPoint(2, 500, 300);
Parameters:
- scale (number) - Target zoom scaleworldX
- (number) - World X coordinate to centerworldY
- (number) - World Y coordinate to center
---
##### reset()
Reset to initial state (zoom=1, pan=0,0).
`javascript`
pz.reset();
---
##### getTransform()
Get current transform state.
`javascriptZoom: ${scale}, Pan: (${panX}, ${panY})
const { scale, panX, panY } = pz.getTransform();
console.log();`
Returns: { scale: number, panX: number, panY: number }
---
##### toWorld(screenX, screenY)
Convert screen coordinates to world coordinates.
`javascriptWorld position: (${world.wx}, ${world.wy})
const world = pz.toWorld(event.clientX, event.clientY);
console.log();`
Parameters:
- screenX (number) - Screen X coordinate (e.g., from clientX)screenY
- (number) - Screen Y coordinate (e.g., from clientY)
Returns: { wx: number, wy: number }
---
##### toScreen(worldX, worldY)
Convert world coordinates to screen coordinates.
`javascriptScreen position: (${screen.sx}, ${screen.sy})
const screen = pz.toScreen(100, 200);
console.log();`
Parameters:
- worldX (number) - World X coordinateworldY
- (number) - World Y coordinate
Returns: { sx: number, sy: number }
---
##### setZoomLimits(min, max)
Set minimum and maximum zoom levels.
`javascript`
pz.setZoomLimits(0.5, 5); // Allow 0.5x to 5x zoom
Parameters:
- min (number) - Minimum zoom scale (default: 0.1)max
- (number) - Maximum zoom scale (default: 10)
---
##### runTests()
Run built-in test suite to verify mathematical correctness.
`javascript`
const allPassed = pz.runTests();
console.log('Tests passed:', allPassed);
Returns: boolean - true if all tests passed
---
#### Events
All events include detailed information in event.detail.
##### panner-zoomer-transform
Fired whenever the transform changes (pan or zoom).
`javascript`
pz.addEventListener('panner-zoomer-transform', (e) => {
console.log(e.detail.scale); // Current zoom
console.log(e.detail.panX); // Current pan X
console.log(e.detail.panY); // Current pan Y
});
Detail: { scale: number, panX: number, panY: number }
---
##### panner-zoomer-pointerdown
Fired when panning starts.
`javascript`
pz.addEventListener('panner-zoomer-pointerdown', (e) => {
console.log('Clicked at world:', e.detail.wx, e.detail.wy);
});
Detail:
`typescript`
{
wx: number, // World X coordinate
wy: number, // World Y coordinate
screenX: number, // Screen X coordinate
screenY: number, // Screen Y coordinate
viewportX: number, // Viewport-relative X
viewportY: number, // Viewport-relative Y
scale: number, // Current zoom
panX: number, // Current pan X
panY: number // Current pan Y
}
---
##### panner-zoomer-pointermove
Fired during panning.
`javascript`
pz.addEventListener('panner-zoomer-pointermove', (e) => {
console.log('Panning to:', e.detail.wx, e.detail.wy);
});
Detail: Same as panner-zoomer-pointerdown
---
##### panner-zoomer-pointerup
Fired when panning ends.
`javascript`
pz.addEventListener('panner-zoomer-pointerup', (e) => {
console.log('Panning ended');
});
Detail: Same as panner-zoomer-pointerdown
---
##### panner-zoomer-wheel
Fired during zoom (mouse wheel).
`javascript`
pz.addEventListener('panner-zoomer-wheel', (e) => {
console.log('Zoomed from', e.detail.oldScale, 'to', e.detail.newScale);
});
Detail:
`typescript`
{
wx: number, // World coordinates under cursor
wy: number,
screenX: number, // Screen coordinates
screenY: number,
viewportX: number, // Viewport-relative coordinates
viewportY: number,
scale: number, // Current zoom (same as newScale)
panX: number, // Current pan
panY: number,
oldScale: number, // Zoom level before
newScale: number // Zoom level after
}
---
#### Attributes
| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| target | string | - | ID of the panner-zoomer element to control (required) |placement
| | string | "ne" | Position: "ne", "nw", "se", or "sw" |
#### Example
`html
`
---
html

`$3
`html
`$3
`html
`$3
`html


``---
Works in all modern browsers that support:
- Web Components (Custom Elements v1)
- Shadow DOM v1
- Pointer Events
This includes Chrome, Firefox, Safari, and Edge.
---
ISC
---
Panner and zoomer interactions are core to modern web applications:
- Image viewers - View high-resolution images
- Maps - Navigate geographic data (Google Maps, OpenStreetMap)
- Diagrams - Explore flowcharts, org charts, network graphs
- Design tools - Create and edit visual content (Figma, Miro)
- Data visualization - Explore large datasets and dashboards
- CAD/Engineering - View technical drawings and schematics
- Games - Navigate game worlds and strategy maps
This component provides a rock-solid foundation for any panner-zoomer interface, with accurate math and industry-standard behavior.