mobile
Pinch Zoom Image
Two-finger pinch to zoom, one-finger pan once zoomed, and a vertical swipe-to-dismiss at rest scale — the standard photo-viewer gesture set.
- mobile
- pinch
- zoom
- image
- dismiss
Built on Pointer Events directly, not a gesture library; distinct from Swipe Media Inspector's single-axis scrub.
Live preview
Quick usage
import { PinchZoomImage } from "@pinky-ui/systems";
<PinchZoomImage src={photo} alt="Product detail" onDismiss={close} />Install
Add @pinky-ui/systems as a dependency, or use the CLI to copy this component's source directly into your project — no dependency to manage, fully editable.
npm install @pinky-ui/systemspnpm add @pinky-ui/systemsyarn add @pinky-ui/systemsnpx pinky-ui add pinch-zoom-imagePrefer to run the whole repository locally instead?
git clone https://github.com/florash/Pinky-UI.git
cd Pinky-UI
npm install
npm run devAccessibility
- Every gesture has a labelled button or keyboard path, and primary controls meet a 44px touch target.
- State remains readable without relying on colour or motion.
Performance
- Interaction state is local and bounded; gesture updates do not require a page-wide render loop.
- Timers and listeners are cleaned up on unmount.
Reduced motion
The same state change resolves immediately; travel, scale and spring motion are removed without removing the interaction.
When to use
- Touch-first product surfaces where the next action should stay close to the hand.
When not to use
- Desktop-only workflows or interactions whose gesture would hide the only available action.
Skill
Purpose
The standard mobile photo-viewer gesture set: two-finger pinch to zoom, one-finger pan once zoomed, and a vertical swipe down at rest scale to dismiss — built on raw Pointer Events, not a gesture library.
Use when
A single image deserves close inspection — a product photo, a document page, a full-screen media viewer opened from a gallery or lightbox.
Avoid
- Thumbnails or any image still part of a scrollable collection — zoom only belongs to a surface the user has explicitly opened
- Images with essential UI overlaid on them (buttons, captions users must reach) unless those controls stay fixed outside the zoomable layer
- As the only way to see detail — pair with a real close button, not just the swipe-down gesture
Interaction
Pinch scales between 1x and maxScale. Above 1x, a single finger pans the image within its bounds. At exactly 1x, a single-finger vertical drag fades and translates the image, and releasing past ~110px calls onDismiss; short of that, it springs back on Jelly's elastic curve. Two-finger and one-finger gestures never fight each other because pan/dismiss only read from a single active pointer.
Accessibility
An explicit close button is always rendered when onDismiss is provided — never rely on the swipe gesture as the only way out. The group has a descriptive label stating both available gestures.
Reduced motion
The release settle (scale/position snap-back) resolves instantly; pinch and pan themselves are direct 1:1 pointer tracking either way, since they are driven by the user's own hand, not autonomous motion.
Performance
All tracking runs through Motion values (scale, x, y) — no React re-render happens during a pinch or pan, only on gesture start/end.
Composition and anti-patterns
Don't nest inside another draggable or swipeable surface (Swipe-to-Dismiss Card Sheet, Swipe Back Gesture) — pointer capture from one will fight the other for the same touch stream.