【问题标题】:React native on layout change (listen view position)对布局更改做出本机反应(监听视图位置)
【发布时间】:2020-06-02 10:37:34
【问题描述】:

我想用平移响应器监听我正在移动的视图的位置。我正在使用 onLayout 道具来获取宽度、高度、x 和 y 位置,但它只在第一次渲染时运行。有什么想法吗?

代码如下:

import React, { useState, useRef } from "react";
import {
  View,
  Animated,
  PanResponder,
  Dimensions,
  StyleSheet,
} from "react-native";

const WINDOW_HEIGHT = Dimensions.get("window").height;

export default function Cropper({ photo }) {
  const [height, setHeight] = useState(WINDOW_HEIGHT / 2); // Use in future

  const pan = useRef(new Animated.ValueXY()).current;

  const panResponder = useRef(
    PanResponder.create({
      onStartShouldSetPanResponder: () => true,
      onPanResponderMove: Animated.event([null, { dy: pan.y }]),
      onPanResponderRelease: () => {
        pan.flattenOffset();
      },
    })
  ).current;

 const onLayout = (event) => {
    const {
      nativeEvent: { layout },
    } = event;

    // Recalculate top and buttom views' height (setNativeProps)
  };

  const panStyle = {
    transform: pan.getTranslateTransform(),
  };

  return (
    <View style={styles.container}>
      <View style={styles.blurView} />
      <Animated.View
        onLayout={(event) => onLayout(event)}
        {...panResponder.panHandlers}
        style={[
          styles.cropper,
          panStyle,
          {
            height: height,
          },
        ]}
      />
      <View style={styles.blurView} />
      <View style={styles.bottomButtonsContainer}></View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  blurView: {
    flex: 1,
    width: "100%",
    backgroundColor: "rgba(0, 0, 0, .9)",
  },
  cropper: {
    width: "100%",
    backgroundColor: "red",
  },
  bottomButtonsContainer: {
    position: "absolute",
    bottom: 0,
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
    width: "100%",
    height: 120,
  },
});

我要做的是在用户移动它时获取中间视图位置,然后重新计算顶部和底部视图的高度。

【问题讨论】:

    标签: javascript css reactjs react-native


    【解决方案1】:

    如果您将 onLayout 作为道具拉入 export default function Cropper({ photo }) { 应该看起来像 export default function Cropper({ photo, onLayout }) {

    否则,我同意您可能需要声明 onLayout 的回复。

    【讨论】:

      【解决方案2】:

      问题:

      当使用样式转换移动 Animated.View 时,未在传入自定义 PanResponder 的 Animated.View 上触发 OnLayout 方法。

      解决方案(完整代码sn-p在底部):

      PanResponder 上的onPanResponderMove 方法添加一个额外的侦听器,可以触发移动事件信息所需的任何方法。

      onPanResponderMove: Animated.event([null, { dy: pan.y }], {listener: ({nativeEvent}) => {onSquareMoved(nativeEvent)}}),
      
      const onSquareMoved = (event) => {
        console.log('square moved changes')
      }
      

      为了在 panResponder 每次移动时调用一个方法,我们添加了一个监听器来监听事件并调用 onSquareXChanged 方法 - 这是 onLayout 的重命名版本。

      我添加了一个完整的零食,每次方块移动时都会调用一个方法,你可以在这里找到它: https://snack.expo.io/@m4qr/spunky-soda

      它使用来自@ajthyng 提供的answer 的代码。 在运行您的零食版本时,我让 onLayout 仅在第一次渲染时触发,而不是在移动 squre 时触发。结合调试结果和以下 github 问题的响应:https://github.com/facebook/react-native/issues/28775,似乎在 Animated.View 中更改样式转换值时不会触发 onLayout。

      如果您在 Animated.View 中的组件更复杂,或者所需的动画在计算方面更繁重,您可能会偶然发现性能问题。如果您有兴趣在不损失性能的情况下添加更复杂的动画效果,我建议您查看当前处于 Alpha 阶段的 Reanimated 2 库。 该库允许您为稍后将在其自己的线程上执行的动画声明逻辑,因为它利用工作集功能允许在其自己的线程上运行动画逻辑,同时保持 60 fps。

      William Candillon 有一个很棒的视频,它用一个类似于上面问题中的零食的例子来探索库的新语法: https://www.youtube.com/watch?time_continue=471&v=e5ALKoP1m-k&feature=emb_title

      如果对此有任何其他问题,请告诉我。

      工作代码的完整示例:

      import React, { useState, useRef } from "react";
      import {
        View,
        Animated,
        PanResponder,
        Dimensions,
        StyleSheet,
      } from "react-native";
      
      const WINDOW_HEIGHT = Dimensions.get("window").height;
      
      export default function Cropper({ photo }) {
        const [height, setHeight] = useState(WINDOW_HEIGHT / 2); // Use in future
      
        const pan = useRef(new Animated.ValueXY()).current;
      
        const panResponder = useRef(
          PanResponder.create({
            onStartShouldSetPanResponder: () => true,
            onPanResponderMove: Animated.event([null, { dy: pan.y }],
              {listener: ({nativeEvent}) => onSquareMoved(nativeEvent)}}),
            onPanResponderRelease: () => {
              pan.flattenOffset();
            },
          })
        ).current;
      
        const onSquareMoved = (event) => {
          console.log('square was moved')
        }
      
        return (
          <View style={styles.container}>
            <View style={styles.blurView} />
            <Animated.View
              onLayout={(event) => onLayout(event)}
              {...panResponder.panHandlers}
              style={[
                styles.cropper,
                panStyle,
                {
                  height: height,
                },
              ]}
            />
            <View style={styles.blurView} />
            <View style={styles.bottomButtonsContainer}></View>
          </View>
        );
      }
      
      const styles = StyleSheet.create({
        container: {
          flex: 1,
        },
        blurView: {
          flex: 1,
          width: "100%",
          backgroundColor: "rgba(0, 0, 0, .9)",
        },
        cropper: {
          width: "100%",
          backgroundColor: "red",
        },
        bottomButtonsContainer: {
          position: "absolute",
          bottom: 0,
          flexDirection: "row",
          justifyContent: "space-between",
          alignItems: "center",
          width: "100%",
          height: 120,
        },
      });
      

      【讨论】:

        【解决方案3】:

        您好像忘记了 constonLayout

        我已经在 snack expo 上尝试过你的代码,我可以在移动红色视图时获取 console.logs

        【讨论】:

        • 是的,这是真的,但我仍然有同样的问题。它仅在组件第一次安装时运行。我想要的是用平移响应器移动中间视图,然后自动获取新的视图位置。
        猜你喜欢
        • 1970-01-01
        • 2018-08-22
        • 1970-01-01
        • 1970-01-01
        • 2015-05-09
        • 2016-03-29
        • 2019-06-30
        • 2021-09-05
        • 1970-01-01
        相关资源
        最近更新 更多