Skip to content

SnapbackZoom

An ideal component for preview handling, as its name suggests it returns to its original position after the pinch gesture ends, you can see this feature being implemented in Telegram's messages containing images and/or videos or Instragram's posts.

The next video footage is a complex use case, this one is taken from the Example app.

What this component is not

This component turns its child component into a zoomable one, that's all, this component nor any component has the ability to bypass zIndex and overflow style's restrictions, it's ability to go over other elements relies completely on you and how you structured your layout to handle such use case.

How to use

Its usage is pretty straight forward, just wrap a component of your choice with it.

Child Component Guidelines

SnapbackZoom's child component must be measurable, therefore avoid the following:

  • Do not use relative size units like {width: '100%'}, use absolute values instead, for instance {width: 200, height: 200}.
  • Do not use {position: 'absolute'} style, wrap SnapbackZoom in an absolute positioned view if you need to.
jsx
import { SnapbackZoom } from "react-native-zoom-toolkit"

// Simple use case
<SnapbackZoom>
  <Image
    source={{ uri: IMAGE }}
    style={{ width: 200, height: 200 }}
    resizeMethod={"scale"}
    resizeMode={"cover"}
  />
</SnapbackZoom>

// Complex use case
<SnapbackZoom
  hitSlop={{ vertical: 50, horizontal: 50 }}
  timingConfig={{ duration: 150, easing: Easing.linear }}
  onTap={(e) => console.log(e)}
  onDoubleTap={(e) => console.log(e)}
  onPinchStart={(e) => console.log(e)}
  onPinchEnd={(e) => console.log(e)}
  onUpdate={(e) => {
    'worklet';
     console.log(e);
  }}
  onGestureEnd={() => console.log('animation finished!')}
>
    <Image
      source={{ uri: IMAGE }}
      style={{ width: 200, height: 200 }}
      resizeMethod={"scale"}
      resizeMode={"cover"}
    />
</SnapbackZoom>

Properties

All properties for this component are optional.

hitslop

TypeDefaultAdditional Info
objectundefinedsee HitSlop

Increase (Android only) or decrease the gesture detection area around your component in all directions by a given amount in pixels, useful when dealing with small components.

timingConfig

TypeDefaultAdditional Info
objectundefinedsee TimingConfig

Tip

Be realistic with the timing configuration you use as you will not be able to resume the gesture once the snapback animation has started.

Custom React Native Reanimated's timing configuration used to snap back to the original position.

resizeConfig

TypeDefault
ResizeConfigundefined

Dynamically recalculates SnapbackZoom component's width and height to align with a given aspect ratio based on a scale value as the gesture scale increases, see About resizeConfig Property for a detailed example.

gesturesEnabled

TypeDefault
booleantrue

Enables or disable all gestures.

onTap

TypeDefaultAdditional Info
(e: TapGestureEvent) => voidundefinedsee tap gesture event data

Callback triggered when the user taps the wrapped component once.

onDoubleTap

TypeDefaultAdditional Info
(e: TapGestureEvent) => voidundefinedsee tap gesture event data

Callback triggered when the user taps the wrapped component twice.

onPinchStart

TypeDefaultAdditional Info
(e: PinchGestureEvent) => voidundefinedsee pinch gesture event data

Callback triggered when the pinch gesture starts.

onPinchEnd

TypeDefaultAdditional Info
(e: PinchGestureEvent) => voidundefinedsee pinch gesture event data

Callback triggered as soon as the user lifts their fingers off the screen after pinching.

onUpdate

TypeDefaultAdditional Info
(state: SnapbackZoomState<number>) => voidundefinedsee worklets

Worklet callback triggered from the moment pinch gesture starts until the snap back animation finishes, ideal if you need to mirror the current state of the gesture to some other component, see SnapbackZoomState.

onGestureEnd

TypeDefault
() => voidundefined

Callback triggered once the snap back animation has finished.

About resizeConfig Property

Before you start reading, for a visual reference watch the video above and pay attention to the parrot image.

Imagine you've got a lot of images you want to display as tiles of 200x200 pixel size, for many of those images the aspect ratio has been compromised, assume one of those images is 1920x1080 pixel size and you would like this image to resize in such a way the aspect ratio is no longer compromised when the image has been scaled two times by the pinch gesture, your component will look like this.

tsx
const resizeConfig = {
  size: { width: 200, height: 200 }, // size of your tile
  aspectRatio: 1920 / 1080, // aspect ratio based on the size of your image/video
  scale: 2 // at which scale the aspect ratio is no longer compromised
}

<SnapbackZoom resizeConfig={resizeConfig}>
  {/* Use width and height properties not flex: 1 */ }
  <SomeImage style={{ width: '100%', height: '100%' }} />
</SnapbackZoom>

Important

Contrary to the child components guidelines mentioned at the start of this page when using resizeConfig property your component must have the following styles {width: '100%', height: '100%'}.

At a scale of one your image is a tile of 200x200 pixel size, in other words a square, but at a scale two it resizes to 340x200 pixel size becoming a rectangle matching with the image's aspect ratio.

Type Definitions

ResizeConfig

Propertytypedescription
size{width: number; height: number;}Fields specify the width and height of your component.
aspectRationumberAspect ratio of your image/video/component.
scalenumberAt which scale your component will be fully resized to meet the aspect ratio.

SnapbackZoomState

NameTypeDescription
xnumberPosition in the x axis starting from the top left corner of the screen
ynumberPosition in the y axis starting from the top left corner of the screen
widthnumberInital width measurement of your component
heightnumberInital height measurement of your component
resizedWidthnumber | undefinedCurrent width measurement of your component, if resizeConfig property is undefined, this value will be undefined too
resizedHeightnumber | undefinedCurrent height measurement of your component, if resizeConfig property is undefined, this value will be undefined too
translateXnumberCurrent translateX transformation value
translateYnumberCurrent translateY transformation value
scalenumberCurrent scale transformation value

Released under the MIT License.