javascript - react native - 放大图像始终放大到中心,而不是用户手指指向的位置

标签 javascript reactjs react-native

我正在努力在 React Native 中显示图像。 我正在使用react-native-gesture-handler和本地人Animated来自react-native的组件。

图像应该可以捏来缩放,并且也可以平移以便在放大后能够四处移动。一切都按预期工作,除了它总是缩放到中心,而且我无法找到一个好的使用方法焦点在缩放和移动时实际更新 X 和 Y。

handlePinchStateChange函数在每次事件更改时运行。如果我们开始缩放,我们可以从事件参数中获取焦点

const handlePinchStateChange = (event) => {
  console.log(event.nativeEvent.focalX, event.nativeEvent.focalY)
}

这一切都很好,但我需要使用这些值来实时更新 X 和 Y,而不是在事件发生之后,如果我只是这样做

translateX = event.nativeEvent.focalY

我怎样才能使平移和捏合都使用焦点,以便它缩放并移动到用户手指所在的位置?

工作零食:https://snack.expo.dev/@sbv/bold-chip

工作代码:

  const pinchRef = React.createRef();
  const panRef = React.createRef();
  const [panEnabled, setPanEnabled] = React.useState(false);

  const translateX = React.useRef(new Animated.Value(0)).current;
  const translateY = React.useRef(new Animated.Value(0)).current;
  const scale = React.useRef(new Animated.Value(1)).current;

  const onPanEvent = Animated.event(
    [
      {
        nativeEvent: {
          translationX: translateX,
          translationY: translateY,
        },
      },
    ],
    {
      useNativeDriver: false,// cannot use nested native drivers in a snack
    }
  );

  const onPinchEvent = Animated.event([{ nativeEvent: { scale } }], {
    useNativeDriver: false, // cannot use nested native drivers in a
  });

  const handlePinchStateChange = (event) => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      setPanEnabled(true);
    }

    if (event.nativeEvent.scale < 1) {
      Animated.spring(scale, {
        toValue: 1,
        useNativeDriver: true,
        bounciness: 2,
      }).start();
      Animated.spring(translateX, {
        toValue: 0,
        useNativeDriver: true,
      }).start();
      Animated.spring(translateY, {
        toValue: 0,
        useNativeDriver: true,
      }).start();
      setPanEnabled(false);
    }
  };

  return (
    <GestureHandlerRootView>
      <PanGestureHandler
        ref={panRef}
        onGestureEvent={onPanEvent}
        enabled={panEnabled}
        simultaneousHandlers={[pinchRef]}>
        <PinchGestureHandler
          onGestureEvent={onPinchEvent}
          onHandlerStateChange={handlePinchStateChange}
          simultaneousHandlers={[panRef]}
          ref={pinchRef}>
          <Animated.Image
            source={{
              uri: 'https://reactnative.dev/img/tiny_logo.png',
            }}
            style={{
              width: '100%',
              height: 300,

              transform: [
                { scale },
                { perspective: 200 },
                { translateX: translateX },
                { translateY: translateY },
              ],
              resizeMode: 'contain',
            }}
          />
        </PinchGestureHandler>
      </PanGestureHandler>
    </GestureHandlerRootView>
  );

最佳答案

嗨,我尝试了相同的解决方案,但得到了相同的结果。 阅读“react-native-gesture-handler”文档和示例,我制作了这个组件并且运行良好。

import React, { useRef, createRef } from "react";
import { View, Animated, StyleSheet } from "react-native";
import {
  PanGestureHandler,
  PinchGestureHandler,
  State
} from "react-native-gesture-handler";

export interface ImageWithZoomAndPanProps {
  imageURI: string;
}

const ImageWithZoomAndPan = ({ imageURI }: ImageWithZoomAndPanProps) => {
  const pinchRef = createRef();
  const panRef = createRef();

  /* Pinching */
  const baseScale = useRef(new Animated.Value(1)).current;
  const pinchScale = useRef(new Animated.Value(1)).current;
  const scale = useRef(Animated.multiply(baseScale, pinchScale)).current;
  let lastScale = 1;

  const onPinchGestureEvent = Animated.event(
    [
      {
        nativeEvent: {
          scale: pinchScale
        }
      }
    ],
    { useNativeDriver: true }
  );

  const handlePinchStateChange = ({ nativeEvent }) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      lastScale *= nativeEvent.scale;
      baseScale.setValue(lastScale);
      pinchScale.setValue(1);
    }
  };

  /* Pan */
  const translateX = useRef(new Animated.Value(0)).current;
  const translateY = useRef(new Animated.Value(0)).current;
  let lastOffset = { x: 0, y: 0 };

  const onPanEvent = Animated.event(
    [
      {
        nativeEvent: {
          translationX: translateX,
          translationY: translateY
        }
      }
    ],
    { useNativeDriver: true }
  );

  const handlePanStateChange = ({ nativeEvent }) => {
    if (nativeEvent.oldState === State.ACTIVE) {
      lastOffset.x += nativeEvent.translationX;
      lastOffset.y += nativeEvent.translationY;

      translateX.setOffset(lastOffset.x);
      translateX.setValue(0);
      translateY.setOffset(lastOffset.y);
      translateY.setValue(0);
    }
  };

  return (
    <View style={styles.mainContainer}>
      <PanGestureHandler
        ref={panRef}
        onGestureEvent={onPanEvent}
        onHandlerStateChange={handlePanStateChange}
        shouldCancelWhenOutside
      >
        <Animated.View style={[styles.wrapper]}>
          <PinchGestureHandler
            ref={pinchRef}
            simultaneousHandlers={panRef}
            onGestureEvent={onPinchGestureEvent}
            onHandlerStateChange={handlePinchStateChange}
          >
            <Animated.View style={styles.imageContainer} collapsable={false}>
              <Animated.Image
                style={[
                  styles.pinchableImage,
                  {
                    transform: [
                      { scale: scale },
                      { translateX },
                      { translateY }
                    ]
                  }
                ]}
                source={{ uri: imageURI }}
              />
            </Animated.View>
          </PinchGestureHandler>
        </Animated.View>
      </PanGestureHandler>
    </View>
  );
};

export default ImageWithZoomAndPan;


const styles = StyleSheet.create({
  mainContainer: {
    width: "100%",
    height: "100%"
  },
  imageContainer: {
    ...StyleSheet.absoluteFillObject,
    backgroundColor: "black",
    overflow: "hidden",
    alignItems: "center",
    flex: 1,
    justifyContent: "center"
  },
  pinchableImage: {
    width: "100%",
    height: "100%"
  },
  wrapper: {
    flex: 1
  }
});

关于javascript - react native - 放大图像始终放大到中心,而不是用户手指指向的位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73220334/

相关文章:

Javascript : accurate CSS TranslateX ( position ) taking Transform Scale into account

reactjs - 防止在 React Native WebView 中重新加载

ios - ld : library not found for -lreact-native-fetch-blob with pods?

javascript - clearTimeout 在嵌套函数中不起作用

javascript - AngularJS:在 AngularJS 中动态生成表单

JavaScript 数组,返回键 :value pairs with specific key

javascript - "You may need an appropriate loader to handle this file type"

node.js - nodemailer 不发送邮件,给出 250 ok

javascript - 在一行上重命名导入数组的键(很像解构)

reactjs - 如何离线使用您的 expo React Native 应用程序而不在终端中运行?