Skip to content

CropZoom

An ideal, practical and unopinionated component for image and video cropping needs, among its features we can find the following:

  • Custom UI: Build your own custom UI on top of it.
  • Resuamble: Resumable and accurate pinch to zoom features; pan, pinch and even pan with the pinch gesture! It will resume where you left.
  • Barebones: For complex use cases, use it as an overlay view and mirror its transformation values to some other components, for instance React Native Skia's components.
  • Fixed size: Enforce all resulting crops to be of a fixed size, ideal for profile pictures.

This component comes with a handy algorithm to perform cropping operations for you, however you will need the help a of deidicated library for this task.

The next video footage is taken from the Example app.

How to use

Managed mode

Managed mode is the default mode and its designed for simple use cases as the one shown in the video footage above.

Its usage is pretty straight forward, just wrap a component of your choice with it, however there are some things to keep in mind:

Remember

  • This component uses flex: 1 style property therefore it will attempt to take all available space, its minimum dimensions are the values provided to cropSize property.
  • The crop area is centered using justifyContent and alignItems center.
  • This component calculates the dimensions needed to meet the resolution property's aspect ratio, therefore your images and videos must use { flex: 1 } style property so they cover the gesture detection area properly.
jsx
import {Image} from 'react-native';
import {CropZoom, useImageResolution, type CropZoomType} from 'react-native-zoom-toolkit`;

const imageUrl = 'url to some image';
const cropSize = {width: 200, height: 200};

// A reference so you can access all methods
const ref = useRef<CropZoomType>(null);

// Utility hook to get the resolution of a network image
const { resolution } = useImageResolution({uri: imageUrl });

// A function that renders an svg with a hole in it.
const renderOverlay = () => <SomeComponent />

if(resolution === undefined) {
  return null;
}

<CropZoom 
  ref={ref}
  debug={true}
  cropSize={cropSize} 
  resolution={resolution} 
  OverlayComponent={renderOverlay}
>
  <Image source={{uri: iamgeUrl }} style={{flex: 1}} />
</CropZoom>

For a detailed managed mode example, see Example App's CropZoom Managed Example

Overlay mode

In constrast to managed mode, overlay mode is designed to provide a barebones component with the very minimum necessary to work for complex use cases; it provides the crop and gesture detection areas only.

There are some things to keep in mind

Remember

  • You lose access to OverlayComponent and children properties.
  • The dimensions for this component are the values passed to cropSize property.
  • Use the debug property so you can see the crop and gesture detection areas as you develop.
  • Create your own shared values and update them with onGestureActive worklet callback.

For an overlay mode example, see Example App's CropZoom Skia Example

Properties

mode

TypeDefaultRequiredAdditional Info
CropModeCropMode.MANAGEDNosee CropMode

Select which one of the two available modes to use.

cropSize

TypeRequired
{ width: number; height: number; }Yes

Size of the cropping area.

resolution

TypeRequired
{ width: number; height: number; }Yes

Resolution of your image, video or how big whatever you are trying to crop really is.

debug

TypeDefaultRequired
booleanfalseNo

Highlights the cropping area with a red-ish color as well as the gesture detection area with a light green color, use it to align your OverlayComponent with the crop area properly.

Note

In case you're color blind and/or have any trouble differentiating colors, please consider opening an issue suggesting a suitable pair of colors.

minScale

TypeDefaultRequired
number1No

Minimum scale value allowed by the pinch gesture, expects values greater than or equals one.

maxScale

TypeDefaultRequired
number-1No

Maximum scale value allowed by the pinch gesture, negative values instruct the component to infer the maximum scale value based on cropSize and resolution properties in a such way maxScale is a value just before images and videos start getting pixelated.

panMode

TypeDefaultRequiredAdditional Info
PanModePanMode.FREENosee PanMode

Select which one of the three available pan modes to use.

scaleMode

TypeDefaultRequiredAdditional Info
ScaleModeScaleMode.BOUNCENosee ScaleMode

Select which one of the two available scale modes to use.

panWithPinch

TypeDefaultRequired
booleantrueNo

Beware iOS users

This feature is disabled by default for iOS users when a version of React Native Gesture Handler prior to 2.16.0 is installed, installing a version greater than equals 2.16.0 will set the value of this property to true by default.

For more information see this Gesture Handler's issue and this issue.

Lets the user drag the component around as they pinch, it also provides a more accurate pinch gesture calculation at the cost of a subtle "staircase feeling", disable for a smoother but less accurate experience.

This feature is not associated with a pan gesture, therefore it won't trigger onPanStart and onPanEnd callbacks while you pinch.

onTap

TypeDefaultRequiredAdditional Info
functionundefinedNosee tap gesture event data

Callback triggered when the user taps the wrapped component once, receives a tap gesture event as its only argument.

onPanStart

TypeDefaultRequiredAdditional Info
functionundefinedNosee pan gesture event data

callback triggered when the pan gesture starts, receives pan gesture event as its only argument.

onPanEnd

TypeDefaultRequiredAdditional Info
functionundefinedNosee pan gesture event data

Callback triggered when the pan gestures ends, receives pan gesture event as its only argument.

onPinchStart

TypeDefaultRequiredAdditional Info
functionundefinedNosee pinch gesture event data

callback triggered when the pinch gesture starts, receives pinch gesture event as its only argument.

onPinchEnd

TypeDefaultRequiredAdditional Info
functionundefinedNosee pinch gesture event data

Callback triggered as soon as the user lift their fingers off the screen after pinching, receives tap gesture event as its only argument.

onGestureActive

TypeDefaultRequiredAdditional Info
worklet functionundefinedNosee worklets

Worklet callback triggered when the internal state of the component changes, the internal state is updated as the user makes use of the gestures or execute its methods, receives an object of type CropZoomState as its only argument.

Ideal if you need to mirror its current transformations values to some other component as it updates.

onGestureEnd

TypeDefaultRequired
functionundefinedNo

Callback triggered when a pan gesture or a pinch gesture ends, if the gesture finished when the wrapped component was not in bounds, this one will wait for the snapback animation to end.

OverlayComponent

TypeDefaultRequired
functionundefinedNo

A function that returns a React Component, such component will sit between your desired component to crop and the gesture detector, for instance you can pass an svg component with a "hole" in it.

Condition

mode property must be set to CropMode.MANAGED (default value).

Methods

All methods are accessible through a ref object.

jsx
import { useRef } from 'react';
import { CropZoom, type CropZoomType } from 'react-native-zoom-toolkit'

const ref = useRef<CropZoomType>(null);
ref.current?.crop(200);

<CropZoom ref={ref} />

crop

Map all the transformations applied to your component into a simple and ready to use object specifying the context necessary for a crop operation, such object must be used along with a library capable of cropping images and/or videos, for instance Expo Image Manipulator, see Use Crop Zoom with Expo Image Manipulator guide.

  • Arguments
NameTypeDefaultDescription
fixedWidthnumber | undefinedundefinedEnforce all resulting crops to be of a fixed width, height is inferred by the computed aspect ratio of CropZoom's cropSize property.

Beware

If you called this method with the fixedWidth argument, resulting crops may be subject to one pixel margin of error, this is an intentional behavior in order to prevent some image cropping libraries from crashing your app.

rotate

Rotate the component 90 degrees clockwise in a range from 0 to 360 degrees.

  • Arguments
NameTypeDefaultDescription
animateboolean | undefinedtrueWhether to animate the transition or not.
cbfunction | undefinedundefinedCallback to trigger at the beginning of the transition, as its only argument receives the angle your component will transition to, this angle ranges from 0 to 360 degrees (at 360 degrees it's clamped to 0).
  • Returns void

flipHorizontal

Flip the component horizontally.

  • Arguments
NameTypeDefaultDescription
animateboolean | undefinedtrueWhether to animate the transition or not.
cbfunction | undefinedundefinedCallback to trigger at the beginning of the transition, as its only argument receives the angle your component will transition to, values are 0 or 180.
  • Returns void

flipVertical

Flip the component vertically.

  • Arguments
NameTypeDefaultDescription
animateboolean | undefinedtrueWhether to animate the transition or not.
cbfunction | undefinedundefinedCallback to trigger at the beginning of the transition, as its only argument receives the angle your component will transition to, values are 0 or 180.
  • Returns void

reset

Reset all transformations to their initial state.

  • Arguments
NameTypeDefaultDescription
animateboolean | undefinedtrueWhether to animate the transition or not.
  • Returns void

requestState

Request internal transformation values of this component at the moment of the calling.

assignState

Assigns the internal transformation values for this component, if the state you have provided is considered to be not achievable by the component's boundaries, it'll be approximated to the closest valid state.

  • Arguments
NameTypeDescription
stateCropZoomAssignableStateObject containg the transformation values to assign to CropZoom component.
animateboolean | undefinedWhether to animate the transition or not, defaults to true.
  • Returns void

Type Definitions

CropZoomState

NameTypeDescription
widthnumberCurrent width of the wrapped component.
heightnumberCurrent height of the wrapped component.
translateXnumberCurrent translateX transformation value.
translateYnumberCurrent translateY transformation value.
rotatenumberCurrent rotate transformation value, angle is measured in radians.
rotateXnumberCurrent rotateX transformation value, angle is measured in radians.
rotateYnumberCurrent rotateY transformation value, angle is measured in radians.

CropZoomAssignableState

NameTypeDescription
translateXnumberTranslateX transformation value.
translateYnumberTranslateY transformation value.
rotatenumberRotate transformation value, angle measured in radians.
rotateXnumberRotateX transformation value, angle measured in radians.
rotateYnumberRotateY transformation value, angle measured in radians.

CropMode Enum

PropertyDescription
MANAGEDMode designed for common uses cases, see How to use.
OVERLAYMode designed for complex use cases, it provides a barebones component, see How to use.

PanMode Enum

Determines how your component must behave when it reaches the specified boundaries by the cropping area.

PropertyDescription
CLAMPPrevents the user from dragging the component out of the specified boundaries.
FREELets the user drag the component around freely, when the pan gesture ends the component will return to a position within the specified boundaries.
FRICTIONLets the user drag the component around applying friction to the pan gesture up to a point where it's stopped completely, when the pan gesture ends the component will return to a position within the specified boundaries.

ScaleMode Enum

Determine how your component must behave when the pinch gesture's scale value exceeds the specified boundaries by minScale and maxScale properties.

PropertyDescription
CLAMPPrevents the scale from exceeding the scale boundaries.
BOUNCELets the user scale above and below the scale boundary values, when the pinch gesture ends the scale value returns to minScale or maxScale respectively.

CropContextResult

PropertyTypeDescription
crop
typescript
{
  originX: number; 
  originY: number; 
  width: number; 
  height: number;
}
Fields specify the top left corner and size of the crop.
context
typescript
{
  rotationAngle: number; 
  flipHorizontal: boolean; 
  flipVertical: boolean;
}
Fields specify if the image/video needs to flipped and to what angle must be rotated, the angle is measured in degrees.
resize
typescript
{
  width: number; 
  height: number; 
} | undefined
Fields specify the size your image/video must be resized to before cropping, if this property is undefined it means no resizing is necessary.

This property is always undefined if you call crop method without fixedWidth argument.

Released under the MIT License.