我想用平移响应器来监听我正在移动的视图的位置,我正在使用onLayout道具来获取宽度、高度、x和y的位置,但只有在第一次渲染时才会运行。我使用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,
},
});
我想做的是当用户移动中间视图时,得到它的位置 然后重新计算顶部和底部视图的高度。
如果你把onLayout作为一个道具来使用的话export default function Cropper({ photo }) {
应该是这样 export default function Cropper({ photo, onLayout }) {
否则,我同意回应中说的,你很可能需要声明 onLayout
.
看来你忘了 const
为您 onLayout
.
我试过你的代码 小吃博览会 当移动红色视图时,我可以得到console.logs。
问题:当Animated.View使用样式转换被移动时,Animated.View的OnLayout方法没有被触发。
当Animated.View使用样式转换移动时,OnLayout方法没有被触发在一个带有自定义PanResponder的Animated.View上。
解决方案(完整的代码片段在底部):
添加一个额外的监听器到 onPanResponderMove
的方法 PanResponder
可以触发任何需要使用移动事件信息的方法。
onPanResponderMove: Animated.event([null, { dy: pan.y }], {listener: ({nativeEvent}) => {onSquareMoved(nativeEvent)}}),
const onSquareMoved = (event) => {
console.log('square moved changes')
}
为了在每次panResponder移动的时候调用一个方法,我们添加了一个listtner来监听事件并调用了 onSquareXChanged
方法--这是一个重命名的 onLayout
.
我已经添加了一个完整的小吃,每次方块移动时都会调用一个方法,你可以在这里找到它。https:/snack.expo.io@m4qrspunky-soda。
它使用的代码来自一个 回答 由 @ajthyng. 在运行你的小吃版本时,我得到的onLayout只在第一次渲染时启动,而不是在移动squre时启动。结合调试的结果和下面github问题的回复。https:/github.comfacebookreact-nativeissues28775。在Animated.View中,当风格转换值被改变时,onLayout似乎不会被触发。
如果你的组件在 Animated.View
是一个比较复杂的动画,或者所需的动画计算量比较大,你可能会遇到性能问题。如果你有兴趣在不损失性能的情况下添加更复杂的动画效果,我建议你看看Reanimated 2库,它目前还在Alpha.该库允许你声明动画逻辑,随后将在自己的线程上执行,因为它利用了worklet功能,允许在自己的线程上运行动画逻辑,同时保持60 fps。
William Candillon有一个很好的视频,通过一个类似于上面问题中的点心的例子来探索新的库的语法。https:/www.youtube.comwatch?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,
},
});