Vize

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.