Pan Zoom
Headless pan and zoom container with pointer, pinch, wheel, and keyboard input.
|
|
| Package |
@vizejs/ui/pan-zoom |
| Maturity |
stable |
| Own the source |
vize lib pull pan-zoom |
| Requires |
context, controllable-state |
| Aliases |
pan zoom, zoomable, zoom pan pinch, image zoom, canvas viewport |
| Covers |
react-zoom-pan-pinch, panzoom, Pointer Events pinch gestures, WheelEvent deltaMode |
Usage
import { PanZoomContent, PanZoomFit, PanZoomReset, PanZoom, PanZoomRoot, PanZoomStatus, PanZoomViewport, PanZoomZoomIn, PanZoomZoomOut } from "@vizejs/ui/pan-zoom";
Or copy the source into your project with vize lib pull pan-zoom (see Source Distribution).
API
PanZoomContent
Source: src/families/media/pan-zoom/pan-zoom-content.vue
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
Transformed content. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered content element. |
PanZoomFit
Source: src/families/media/pan-zoom/pan-zoom-fit.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for icon-only content. Without slot content the localized messages.fit label is rendered as text. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to keep the transform. |
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
Button content. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
PanZoomReset
Source: src/families/media/pan-zoom/pan-zoom-reset.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for icon-only content. Without slot content the localized messages.reset label is rendered as text. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to keep the transform. |
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
Button content. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
PanZoom
Source: src/families/media/pan-zoom/pan-zoom-root.vue
Props
| Prop |
Type |
Default |
Description |
modelValue |
PanZoomTransform |
undefined |
Controlled transform (v-model). undefined selects uncontrolled behavior. |
defaultValue |
PanZoomTransform |
{ x: 0, y: 0, scale: 1 } |
Initial uncontrolled transform, also restored by reset() and the 0 key. |
minScale |
number |
0.25 |
Smallest zoom factor. |
maxScale |
number |
8 |
Largest zoom factor. |
bounds |
PanZoomBounds |
"none" |
Pan and zoom constraint applied to every change. |
zoomStep |
number |
1.25 |
Multiplicative factor of one zoom-in or zoom-out step. |
panStep |
number |
40 |
Viewport pixels moved by one arrow key press (Shift moves four times as far). |
wheelMode |
PanZoomWheelMode |
"zoom" |
Wheel behavior. Trackpad pinches (ctrlKey wheels) always zoom. |
doubleClickZoom |
boolean |
true |
Zoom in one step around the pointer on double-click (Shift zooms out). |
fitPadding |
number |
0 |
Inset kept around the content by fit() and the Home key. |
contentSize |
PanZoomSize |
undefined |
Unscaled content size. undefined measures the PanZoomContent element. |
disabled |
boolean |
false |
Suppress every pointer, wheel, keyboard, and button interaction. |
messages |
PanZoomMessages |
undefined |
Localized role description, button labels, and zoom announcement. |
Events
| Event |
Payload |
Description |
update:modelValue |
[transform: PanZoomTransform] |
Fired with every requested transform. |
change |
[transform: PanZoomTransform, previous: PanZoomTransform, source: PanZoomChangeSource] |
Fired after every distinct transform request with its source. |
transformStart |
[source: PanZoomChangeSource] |
Fired when a continuous gesture (pan, pinch, wheel burst) starts. |
transformEnd |
[source: PanZoomChangeSource] |
Fired when a continuous gesture ends. |
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
Viewport, content, controls, and status. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
canZoomIn |
— |
|
canZoomOut |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
fit |
— |
|
panBy |
— |
|
reset |
— |
|
scale |
— |
|
setTransform |
— |
|
state |
— |
|
transform |
— |
|
zoomIn |
— |
|
zoomOut |
— |
|
zoomTo |
— |
|
PanZoomRoot
Source: src/families/media/pan-zoom/pan-zoom-root.vue
Props
| Prop |
Type |
Default |
Description |
modelValue |
PanZoomTransform |
undefined |
Controlled transform (v-model). undefined selects uncontrolled behavior. |
defaultValue |
PanZoomTransform |
{ x: 0, y: 0, scale: 1 } |
Initial uncontrolled transform, also restored by reset() and the 0 key. |
minScale |
number |
0.25 |
Smallest zoom factor. |
maxScale |
number |
8 |
Largest zoom factor. |
bounds |
PanZoomBounds |
"none" |
Pan and zoom constraint applied to every change. |
zoomStep |
number |
1.25 |
Multiplicative factor of one zoom-in or zoom-out step. |
panStep |
number |
40 |
Viewport pixels moved by one arrow key press (Shift moves four times as far). |
wheelMode |
PanZoomWheelMode |
"zoom" |
Wheel behavior. Trackpad pinches (ctrlKey wheels) always zoom. |
doubleClickZoom |
boolean |
true |
Zoom in one step around the pointer on double-click (Shift zooms out). |
fitPadding |
number |
0 |
Inset kept around the content by fit() and the Home key. |
contentSize |
PanZoomSize |
undefined |
Unscaled content size. undefined measures the PanZoomContent element. |
disabled |
boolean |
false |
Suppress every pointer, wheel, keyboard, and button interaction. |
messages |
PanZoomMessages |
undefined |
Localized role description, button labels, and zoom announcement. |
Events
| Event |
Payload |
Description |
update:modelValue |
[transform: PanZoomTransform] |
Fired with every requested transform. |
change |
[transform: PanZoomTransform, previous: PanZoomTransform, source: PanZoomChangeSource] |
Fired after every distinct transform request with its source. |
transformStart |
[source: PanZoomChangeSource] |
Fired when a continuous gesture (pan, pinch, wheel burst) starts. |
transformEnd |
[source: PanZoomChangeSource] |
Fired when a continuous gesture ends. |
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
Viewport, content, controls, and status. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
transform |
PanZoomTransform |
Current transform. |
scale |
number |
Current zoom factor. |
state |
PanZoomState |
Current interaction state. |
canZoomIn |
boolean |
Whether zooming in is possible. |
canZoomOut |
boolean |
Whether zooming out is possible. |
element |
HTMLDivElement | null |
Rendered root element. |
setTransform |
(transform: PanZoomTransform) => boolean |
Request a transform (constrained by scale limits and bounds). Reports a change. |
zoomTo |
(scale: number, point?: PanZoomPoint) => boolean |
Zoom to scale around a viewport point (default: the viewport center). |
zoomIn |
(point?: PanZoomPoint) => boolean |
Zoom in by one zoomStep around a viewport point (default: center). |
zoomOut |
(point?: PanZoomPoint) => boolean |
Zoom out by one zoomStep around a viewport point (default: center). |
panBy |
(dx: number, dy: number) => boolean |
Translate by a viewport-pixel delta. |
reset |
() => boolean |
Restore defaultValue. |
fit |
() => boolean |
Scale and center the content inside the viewport. |
PanZoomStatus
Source: src/families/media/pan-zoom/pan-zoom-status.vue
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState & { readonly text: string } |
Announcement content. Defaults to the localized settled zoom level, updated when a gesture ends rather than on every frame. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered live region. |
text |
string |
Announced text. |
PanZoomViewport
Source: src/families/media/pan-zoom/pan-zoom-viewport.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name of the pan and zoom area. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the area. |
ariaDescribedby |
string |
undefined |
Space-separated ids of usage instructions (e.g. "Arrow keys pan, plus and minus zoom"). |
wheelSettleDelay |
number |
150 |
Quiet period after the last wheel event before transformEnd fires. |
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
PanZoomContent and overlays. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered viewport element. |
PanZoomZoomIn
Source: src/families/media/pan-zoom/pan-zoom-zoom-in.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for icon-only content. Without slot content the localized messages.zoomIn label is rendered as text. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to keep the transform. |
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
Button content. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
PanZoomZoomOut
Source: src/families/media/pan-zoom/pan-zoom-zoom-out.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name for icon-only content. Without slot content the localized messages.zoomOut label is rendered as text. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to keep the transform. |
Slots
| Slot |
Slot props |
Description |
default |
PanZoomSlotState |
Button content. Receives the current transform state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Behavior
Normative state x input -> outcome table for pan-zoom-root.vue,
pan-zoom-viewport.vue, pan-zoom-content.vue, pan-zoom-zoom-in.vue,
pan-zoom-zoom-out.vue, pan-zoom-reset.vue, pan-zoom-fit.vue, and
pan-zoom-status.vue (@vizejs/ui/pan-zoom). Every row is proven by the named test.
The transform maps content to viewport pixels: viewport = content * scale + (x, y).
Every change passes through the scale limits and bounds before it is requested.
PanZoomContent applies transform: translate(x, y) scale(s) with
transform-origin: 0 0 inline (positioning mechanics) and publishes
--vize-ui-pan-zoom-x, --vize-ui-pan-zoom-y, and --vize-ui-pan-zoom-scale.
Consumers own the clipping box, e.g. [data-vize-ui="pan-zoom-viewport"] { overflow: hidden }.
| ID |
State |
Input |
Outcome |
Evidence |
| PZ1 |
idle |
render / mount |
focusable role="group" viewport with localized role description, inline transform + custom properties, labelled controls, polite status |
renders a labelled focusable viewport, transformed content, controls, and live status |
| PZ2 |
idle |
one pointer drag |
pans by the pointer delta (pointer), data-state="panning", emits transformStart/transformEnd; secondary mouse buttons are ignored |
single-pointer drags pan with start and end events |
| PZ3 |
two pointers |
spread / move / release one |
scales by the distance ratio around the midpoint (pinch), then continues as a pan from the remaining pointer |
two pointers pinch around their midpoint and fall back to panning |
| PZ4 |
wheelMode="zoom" |
wheel / ctrl-wheel |
zooms around the cursor (line and page deltas normalized); trackpad pinches use a higher sensitivity; one start/end pair per burst |
wheel zooms around the cursor, trackpad pinches zoom, and bursts settle once |
| PZ5 |
other wheel modes |
wheel |
pan scrolls the content (ctrl still zooms); zoom-with-ctrl leaves unmodified wheels to the page |
wheel modes pan or defer to page scrolling without modifiers |
| PZ6 |
doubleClickZoom |
double-click (+Shift) |
zooms one step in (out) around the pointer; disabled by doubleClickZoom=false |
double-click zooms in one step at the pointer and Shift zooms out |
| PZ7 |
focused viewport |
arrows / + = - / 0 / Home |
arrows pan by panStep (Shift ×4), zoom keys zoom around the center, 0 resets, Home fits; keys from content or with modifiers are ignored |
keyboard pans, zooms around the center, resets, and fits |
| PZ8 |
controls |
click |
zoom one step around the center, reset, or fit; zoom buttons disable at the effective limits; the status announces the settled level |
buttons zoom by one step, reset, fit, and disable at the scale limits |
| PZ9 |
bounds |
any change |
contain centers smaller axes and blocks gaps; cover raises the minimum scale to cover the viewport |
bounds constrain every change |
| PZ10 |
controlled |
any change |
emits the requested transform while rendering the controlled one until the parent accepts it |
controlled transforms win until the parent accepts the request |
| PZ11 |
disabled |
pointer / wheel / keys / clicks |
every interaction is ignored, the viewport leaves the tab order, buttons disable; the imperative API still applies |
disabled roots ignore input but keep the imperative API |
| PZ12 |
controls |
click with preventDefault() |
the transform is unchanged |
controls honor preventDefault from click listeners |
| PZ13 |
messages |
render |
role description, button labels, and the zoom announcement are localized |
messages localize the role description, labels, and zoom announcement |
| PZ14 |
exposed instance |
imperative calls |
setTransform, zoomTo, zoomIn, zoomOut, panBy, reset, fit report whether the transform changed |
exposes typed state and imperative transform controls |
| PZ15 |
missing provider |
setup |
compound parts fail closed with the shared context diagnostic |
compound parts require a matching root provider |
| PZ16 |
transform math |
pure helpers |
zoom anchoring, clamping, contain/cover/region bounds, fit, wheel normalization, and pinch math are deterministic |
pan-zoom-transform.test.ts |
| PZ17 |
SSR |
isolated requests |
markup is byte-identical, includes the initial transform, and omits client-only touch-action |
renders byte-identical pan-zoom markup across isolated SSR requests |
| PZ18 |
SSR / hydration |
hydrate |
server markup hydrates without warnings or node replacement |
hydrates pan-zoom markup without warnings or node replacement |
| PZ19 |
types |
compile |
transforms, bounds, modes, sources, messages, and exposes are closed and read-only |
pan-zoom.types.test-d.ts |
Listeners, touch-action: none, and pointer capture are client-only. Momentum and a
minimap are intentionally out of scope.