如何通过反应重新激活来更新缩放焦点原点并反应本机手势处理程序以进行迭代捏合手势?

GNG*_*GNG 3 animation gesture react-native react-native-reanimated react-native-gesture-handler

我使用react-native-gesture-handler和react-native-reanimated构建了捏合缩放效果。用户可以在图像的任意位置捏合,以手指之间的位置为缩放原点进行放大或缩小。这很棒。我遇到的问题是允许用户以多个捏合手势放大或缩小。这需要记住用户之前的捏合手势的偏移量和缩放比例。使用我当前拥有的代码,当用户第二次捏合时,手势处理程序会记住第一次捏合手势的缩放比例值,不会正确更新缩放原点。如何在不增加转换语句数量的情况下解决此问题?

  const prevZoomScale = useSharedValue(1)
  const currZoomScale = useSharedValue(1)
  const zoomScale = useDerivedValue(() => { return prevZoomScale.value * currZoomScale.value }, [prevZoomScale.value, currZoomScale.value])
  const tempZoomScale = useSharedValue(1)
  const prevOriginOffset = useSharedValue({x: 0, y: 0})
  const tempOriginOffset = useSharedValue({x: 0, y: 0})
  const currOriginOffset = useSharedValue({x: 0, y: 0})
  const pinchOriginOffset = useDerivedValue(() => 
     { 
        return {
                 x: (prevOriginOffset.value.x + currOriginOffset.value.x), 
                 y: (prevOriginOffset.value.y + currOriginOffset.value.y)
        }
    }, 
     [prevOriginOffset.value.x,  prevOriginOffset.value.y,  currOriginOffset.value.x,  currOriginOffset.value.y]
  )

  const onPinchEvent = useAnimatedGestureHandler<PinchGestureHandlerGestureEvent>({
    onStart: (_) => {
      prevZoomScale.value = tempZoomScale.value
      currZoomScale.value = 1
      prevOriginOffset.value = tempOriginOffset.value
      currOriginOffset.value = {x: _.focalX - SIZE / 2, y: _.focalY - SIZE / 2}
    },
    onActive: (event) => {
      if ((event.scale * prevZoomScale.value) > 1) {
        currZoomScale.value = event.scale
      }
    },
    onEnd: (_) => {
      tempZoomScale.value = zoomScale.value
      tempOriginOffset.value = pinchOriginOffset.value
    },


  const animatedStyle = useAnimatedStyle(
    () => ({
      transform: [
        {
          translateX: (pinchOriginOffset.value.x)
        },
        {
          translateY:  (pinchOriginOffset.value.y)
        },
        {
          scale: zoomScale.value
        },
        {
          translateX: - (pinchOriginOffset.value.x)
        },
        {
          translateY: - ( pinchOriginOffset.value.y)
        }
      ],
    }),
    []
  )

    return (
      <View style={[styles.zoomScrollContainer, { backgroundColor: color.core.black }]}>
        <PinchGestureHandler
          onGestureEvent={onPinchEvent}
        >
              <Animated.View >
                <Animated.Image
                  source={{ uri: zoomedImageUri }}
                  style={[styles.imageStyle, animatedStyle]}
                >
                </Animated.Image>
              </Animated.View>
        </PinchGestureHandler>
      </View>
    )
Run Code Online (Sandbox Code Playgroud)

Joh*_*cer 6

我们最近遇到了与此非常相似的事情,试图在 React Native 中制作 Canvas 类型元素。我们在漫长的(!)天后解决了这个问题,所以我\xe2\x80\x99在下面概述了我们的思考过程,并给出了希望可以工作的代码。我们还支持平移和缩放,但我\xe2\x80\x99从下面的代码中删除了任何平移逻辑,因为看起来你不需要它。

\n

我们最初做了你\xe2\x80\x99正在做的事情,试图跟踪用户所做的每个偏移/缩放,但发现它导致了奇怪的结果,我们无法\xe2\x80\x99解决如何结合转变。当用户松开捏合点时,对象会跳到新位置,因为净焦点不正确。

\n

现在,我们在用户捏合时跟踪比例和净 x,y 值,然后在捏合结束时更容易将当前变换与先前的变换组合起来。

\n

我们分别跟踪 X 和 Y 分量,但它们可以轻松组合成一个 {x,y} 对象。

\n

我们还确保屏幕上有 2 个手指,因为在捏合结束时,如果两个手指没有同时从屏幕上移开,焦点可能会跳转到单个手指。

\n

让我知道以下内容是否适合您!

\n
import React from "react";\nimport { View } from "react-native";\nimport {\n    PinchGestureHandler,\n} from "react-native-gesture-handler";\nimport Animated, {\n    useAnimatedStyle,\n    useAnimatedGestureHandler,\n    useSharedValue,\n} from "react-native-reanimated";\n\n\nexport default function Canvas() {\n    const WIDTH = 400;\n    const HEIGHT = 400;\n\n    const focalX = useSharedValue(0);\n    const focalY = useSharedValue(0);\n    const xCurrent = useSharedValue(0);\n    const yCurrent = useSharedValue(0);\n    const xPrevious = useSharedValue(0);\n    const yPrevious = useSharedValue(0);\n    const scaleCurrent = useSharedValue(1);\n    const scalePrevious = useSharedValue(1);\n\n    const pinchHandler = useAnimatedGestureHandler({\n        onStart: (event) => {\n            if (event.numberOfPointers == 2) {\n                focalX.value = event.focalX;\n                focalY.value = event.focalY;\n            }\n        },\n        onActive: (event) => {\n            if (event.numberOfPointers == 2) {\n                // On Android, the onStart event gives 0,0 for the focal\n                // values, so we set them here instead too.\n                if (event.oldState === 2) {\n                    focalX.value = event.focalX;\n                    focalY.value = event.focalY;\n                }\n                scaleCurrent.value = event.scale;\n\n                xCurrent.value = (1 - scaleCurrent.value) * (focalX.value - WIDTH / 2);\n                yCurrent.value = (1 - scaleCurrent.value) * (focalY.value - HEIGHT / 2);\n            }\n        },\n        onEnd: () => {\n            scalePrevious.value = scalePrevious.value * scaleCurrent.value;\n\n            xPrevious.value = scaleCurrent.value * xPrevious.value + xCurrent.value;\n            yPrevious.value = scaleCurrent.value * yPrevious.value + yCurrent.value;\n\n            xCurrent.value = 0;\n            yCurrent.value = 0;\n\n            scaleCurrent.value = 1;\n        },\n    });\n\n    const animatedStyle = useAnimatedStyle(() => {\n        return {\n            transform: [\n                { translateX: xCurrent.value },\n                { translateY: yCurrent.value },\n                { scale: scaleCurrent.value },\n                { translateX: xPrevious.value },\n                { translateY: yPrevious.value },\n                { scale: scalePrevious.value },\n            ],\n        };\n    });\n\n    return (\n        <View>\n            <PinchGestureHandler onGestureEvent={pinchHandler}>\n                <Animated.View style={{width: 1000, height: 1000}}>\n                    <Animated.Image\n                        source={{uri:<IMAGE_URI>}}\n                        style={[{\n                            width: WIDTH,\n                            height: HEIGHT,\n                        },animatedStyle]}\n                    >\n                    </Animated.Image>\n                </Animated.View>\n            </PinchGestureHandler>\n        </View>\n    );\n}\n
Run Code Online (Sandbox Code Playgroud)\n