ZoomPan
3 partsDemo
Anatomy
Import the parts and compose them. Each part forwards attributes to its underlying element.
import {
ViewportRoot,
ViewportSurface,
ViewportContent,
} from '@robonen/primitives/canvas/zoom-pan';
<ViewportRoot>
<ViewportSurface />
<ViewportContent />
</ViewportRoot>API Reference
ViewportRoot
ViewportRoot| Prop | Type | Description |
|---|---|---|
viewport? | Viewport | undefined | Two-way bound value (v-model:viewport). |
| Event | Payload | Description |
|---|---|---|
update:viewport | [value: Viewport | undefined] | Emitted when v-model:viewport updates. |
| Name | Type | Description |
|---|---|---|
getViewport | () => Viewport | Current viewport {x,y,zoom}. |
setViewport | (viewport: Viewport) => void | Replace the viewport (clamped). |
zoomIn | (factor?: number) => void | Zoom in, anchored at the surface centre. |
zoomOut | (factor?: number) => void | Zoom out, anchored at the surface centre. |
zoomTo | (zoom: number) => void | Zoom to an absolute level, anchored at the surface centre. |
zoomToPoint | (zoom: number, point: XYPosition) => void | Zoom to an absolute level, anchored at a surface-relative pixel point. |
fit | (bounds?: Rect | null, params?: FitParams) => void | Fit bounds (content space) into view, centred with padding. Defaults to
the root contentExtent; an explicit no-op when both are null. Respects the
surface width === 0 || height === 0 early-return. |
center | (point?: XYPosition) => void | Centre a content-space point in the surface, keeping the current zoom. |
panBy | (dx: number, dy: number) => void | Pan by a screen-pixel delta. |
reset | () => void | Reset to { x: 0, y: 0, zoom: 1 } (clamped). |
screenToContent | (point: XYPosition) => XYPosition | Convert a screen (client) point to content space. |
contentToScreen | (point: XYPosition) => XYPosition | Convert a content-space point to a screen (client) point. |
viewport | WritableComputedRef<Viewport, Viewport> | — |
ViewportSurface
ViewportSurfaceThe clipping / interaction pane. Clips the content (`overflow: hidden`), disables native touch gestures (`touch-action: none`), reports its live bounding rect into the context as the screen origin for coordinate math, and hosts the wheel / pinch / drag / dblclick / keyboard pan-zoom layer (`useZoomPan`). Carries `role` / `tabindex` for the a11y keyboard layer and reflects gesture state via `data-panning` / `data-zooming` / `data-measured`. Rendered by `ViewportRoot` by default; place directly only when composing the parts manually.
No props or events — renders its element and forwards attributes.
ViewportContent
ViewportContentThe single transformed layer. Everything a consumer renders inside the viewport lives here, so pan/zoom is one GPU-composited `transform` rather than a per-element restyle. `transform-origin: 0 0` is required — the coordinate formulas in `utils.ts` assume top-left scaling. `will-change: transform` is toggled on ONLY while interacting (never permanently): a pinned hint locks the compositor's raster scale, so the cached texture is GPU-upscaled and the content blurs at high zoom — toggling it lets the layer re-rasterise crisply once motion settles (see `useInteractionState`). Pure presentational; renders the default slot.
No props or events — renders its element and forwards attributes.