Surafel
Surafel

Reputation: 147

React-Native Pinch Gesture Handler Zoom Position

I have a Pan gesture handler inside a Pinch gesture handler to create the effects of multi-directional scrolling zoomable view that is all working well. But I am having issues where when users zoom in, the view is not zoomed in under their fingers rather when users zoom, the view would zoom some place else and not keep the same position. Here is my code:

Here is a reproducible demo: Expo Demo

This is the view:

<PinchGestureHandler
          simultaneousHandlers={[panRef]}
          ref={pinchRef}
          onGestureEvent={pinchGestureHandler}>
          <Animated.View style={animatedStyle}>
            <PanGestureHandler
              simultaneousHandlers={[pinchRef]}
              ref={panRef}
            onGestureEvent={panGestureHandler}></PanGestureHandler>
        </Animated.View>
        </PinchGestureHandler>

and here are my handlers:

const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const scale = useSharedValue(1);
  const panRef = useRef();
  const pinchRef = useRef();

  const panGestureHandler = useAnimatedGestureHandler({
    onStart: (_, ctx) => {
      ctx.startX = translateX.value;
      ctx.startY = translateY.value;
    },
    onActive: (event, ctx) => {
      translateX.value = ctx.startX + event.translationX;
      translateY.value = ctx.startY + event.translationY;
    },
    onEnd: (event, ctx) => {
      translateX.value = withDecay({
        velocity: event.velocityX,
        deceleration: 0.99,
      });
      translateY.value = withDecay({
        velocity: event.velocityY,
        deceleration: 0.99,
      });
    },
  });

  const pinchGestureHandler = useAnimatedGestureHandler({
    onStart: (_, ctx) => {
      ctx.startScale = scale.value;
    },
    onActive: (event, ctx) => {
      scale.value = ctx.startScale * event.scale;
    },
    onEnd: (event, ctx) => {
      scale.value = withSpring(Math.min(Math.max(scale.value, 1), 3));
    },
  });

  const animatedStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {translateX: translateX.value},
        {translateY: translateY.value},
        {scale: scale.value},
      ],
    };
  });

ATTEMPTS:

Tried adjusting the handlers to consider the focal points but the zooming is still way off from the point of the fingers

const pinchGestureHandler = useAnimatedGestureHandler({
    onStart: (_, ctx) => {
      ctx.startScale = scale.value;
      ctx.startTranslateX = translateX.value;
      ctx.startTranslateY = translateY.value;
    },
    onActive: (event, ctx) => {
      scale.value = ctx.startScale * event.scale;


      const focalX = event.focalX;
      const focalY = event.focalY;
      
      // Adjust translations
      translateX.value = ctx.startTranslateX + (1 - event.scale) * (focalX - ctx.startTranslateX);
      translateY.value = ctx.startTranslateY + (1 - event.scale) * (focalY - ctx.startTranslateY);
    },
    onEnd: (_) => {
      scale.value = withSpring(Math.min(Math.max(scale.value, 1), 3));
    },
});

Upvotes: 9

Views: 1724

Answers (1)

Enzo Manuel Mangano
Enzo Manuel Mangano

Reputation: 696

Actually I think that the idea of using focalPoints is great. But I would do that differently.

Maybe you can create two Shared Values:

const focalX = useSharedValue(0);
const focalY = useSharedValue(0);

Then in the pinch:

const pinchGestureHandler = useAnimatedGestureHandler({
    onActive: (event, ctx) => {
      scale.value = event.scale;

      focalX.value = event.focalX;
      focalY.value = event.focalY;
      
      // No need to adjust translations manually
      // translateX.value = ctx.startTranslateX + (1 - event.scale) * (focalX - ctx.startTranslateX);
      // translateY.value = ctx.startTranslateY + (1 - event.scale) * (focalY - ctx.startTranslateY);
    },
    onEnd: (_) => {
      scale.value = withSpring(Math.min(Math.max(scale.value, 1), 3));
    },
});

And in the animated style you can try:

 const animatedStyle = useAnimatedStyle(() => {
    return {
      transform: [
          { translateX: -focalX.value },
          { translateY: -focalY.value},
          { scale: scale.value },
          { translateY: focalY.value},
          { translateX: focalX.value},
          { translateX: translateX.value },
          { translateY: translateY.value },
      ],
    };
  });

Basically before scaling I'm just centering the scale transformation with the focal point position.

Hopefully this works :)

Upvotes: 0

Related Questions