Picoral
Picoral

Reputation: 318

How to zoom camera on pinch on react native?

I'm building a RN app using Expo, which uses expo-camera's Camera component. I'm trying to implement zoom on pinch functionality, which basically is computing a number between 0 (no zoom) and 1 (max zoom) based on the gesture.

MRE: https://snack.expo.dev/@fpicoral/expo-camera-zoom

// from https://stackoverflow.com/a/72524084/10893256
const onPinch = useCallback((event: GestureUpdateEvent<PinchGestureHandlerEventPayload>) => {
        const velocity = event.velocity / 20;

        let newZoom =
            velocity > 0
                ? zoom + event.scale * velocity * (Platform.OS === 'ios' ? 0.01 : 25) 
                : zoom - event.scale * Math.abs(velocity) * (Platform.OS === 'ios' ? 0.02 : 50);

        if (newZoom < 0) newZoom = 0;
        else if (newZoom > 0.5) newZoom = 0.5;

        setZoom(newZoom);
    },
    [zoom, setZoom]
);

This is working decently well, but the problem is zooming out. If you never let your fingers off the screen, everything is smooth. However, if you zoom in a lot, lift your fingers, and try to zoom out, it takes a lot of pinches to zoom out properly.

This problem is related to the fact that event.scale will be too small if the user stops the gesture after zooming in. I tried to keep track of the cumulative scale, resetting it when the gesture swapped between zoom in and zoom out, but it didn't help either.

EDIT: It's working decently well on iOS only. On Android, even with the multiplier, it's not zooming in almost at all. When I kept track of the cumulative sum, both iOS and Android had the same behavior, but the problem of zooming out was still present in both platforms.

Upvotes: 1

Views: 2247

Answers (1)

Adam
Adam

Reputation: 31

I've had the same problem with zooming out. I solved it by adding zoom out factor depending on the last zoom (bigger zoom = bigger factor):

    const onPinch = useCallback(
    (event) => {
      const velocity = event.velocity / 20;
      const outFactor = lastZoom * (Platform.OS === 'ios' ? 40 : 15);

      let newZoom =
        velocity > 0
          ? zoom + event.scale * velocity * (Platform.OS === 'ios' ? 0.01 : 25)
          : zoom - (event.scale * (outFactor || 1)) * Math.abs(velocity) * (Platform.OS === 'ios' ? 0.02 : 50);

      if (newZoom < 0) newZoom = 0;
      else if (newZoom > 0.7) newZoom = 0.7;

      setZoom(newZoom);
    },
    [zoom, setZoom, lastZoom, setLastZoom]
  );

  const onPinchEnd = useCallback(
    (event) => {
      setLastZoom(zoom);
    },
    [zoom, setLastZoom]
  );

  const pinchGesture = useMemo(
    () => Gesture.Pinch().onUpdate(onPinch).onEnd(onPinchEnd),
    [onPinch, onPinchEnd]
  );

hope it helps

Upvotes: 3

Related Questions