We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
什么场景下需要获取元素的位置信息?
如何获取子元素相对父元素的位置信息
如何获取元素相对屏幕的位置信息
这些方案有什么区别?
如何理解screen 和 window 的区别?
如上demo 这是一个使用React Native构建的动画示例应用。它包含两个视图,一个红色视图(A)和一个蓝色视图(B)。我们需要将A移动到B的位置。
这个时候我们就需要知道A的位置和B的位置 然后通过react native提供的动画api, 在X轴上从其原始位置移动到与B一致的位置 以实现这个功能。
所以我们该如何获取A的位置信息和B的位置信息呢?
React Native采用的是CSS in JS的方案。所有的样式都是通过JavaScript的对象来定义的,每个组件的样式都是私有的,可以避免不同组件之间的样式冲突。相同的样式可以被抽象成一个JavaScript对象,然后在不同的组件中重复使用。
由于样式是通过JS来定义的,所以可以根据状态或者属性动态生成样式。
import React, { useRef } from 'react'; import { Animated, Easing, StyleSheet, Text, View } from 'react-native'; const styles = StyleSheet.create({ A: { alignItems: 'center', backgroundColor: 'red', height: 50, justifyContent: 'center', left: 100, position: 'absolute', top: 50, width: 50, zIndex: 100, }, B: { position: 'absolute', alignItems: 'center', backgroundColor: 'blue', height: 50, justifyContent: 'center', left: 300, top: 50, width: 50, }, page: { backgroundColor: 'white', flex: 1, position: 'relative', }, text: { color: 'white', fontSize: 20, }, }); const App = () => { const aValue = useRef(new Animated.Value(0)).current; setTimeout(() => { const ani = Animated.timing(aValue, { toValue: 1, duration: 1000, easing: Easing.bezier(0.4, 0, 0.2, 1), useNativeDriver: true, }); ani.start(() => { setTimeout(() => { aValue.setValue(0); }, 1.5 * 1000); }); }, 3 * 1000); return ( <View style={styles.page}> <Animated.View style={[ styles.A, { transform: [ { translateX: aValue.interpolate({ inputRange: [0, 0.5, 1], outputRange: [0, 80, 200], }), }, ], }, ]} > <Text style={styles.text}>A</Text> </Animated.View> <View style={styles.B}> <Text style={styles.text}>B</Text> </View> </View> ); }; export default App;
由于样式就是一个js对象,我们可以动态的修改A元素在样式对象,因为是开发者设置的绝对布局的定位信息,所以我们可以知道B元素的位置信息。
通过动态样式属性的方法 我们可以准确的拿到在同一个参考体系下的两个元素的相对位置信息。例如A,B 都是绝对定位且都是相对于left设置的偏移。
如果将B的样式改为如下代码
const styles = StyleSheet.create({ B: { position: 'absolute', alignItems: 'center', backgroundColor: 'blue', height: 50, justifyContent: 'center', right: 50, // 这里改为right top: 50, width: 50, } });
现在我们不能直接知道视图A和视图B之间的距离,因为视图B的位置是相对于屏幕右侧确定的,而视图A的动画位置则是相对于父窗口左侧确定的。 因为在不同的屏幕宽度下 A,B的相对距离是会发生变化的 所以我们不能通过动态样式的方式直接感知到距离是多少。
这种情况下我们可以使用react native 提供给我们的onLayout回调方法。当改变屏幕方向或者改变布局的时候,该函数会被触发,并且回传给我们一个包含layout属性的对象。layout属性是一个包含x,y,width,height的对象。
const styles = StyleSheet.create({ B: { alignItems: 'center', backgroundColor: 'blue', height: 50, justifyContent: 'center', position: 'absolute', right: 50, top: 50, width: 50, }, }); const App = () => { const aValue = useRef(new Animated.Value(0)).current; const layoutA = useRef<LayoutRectangle>(null); const layoutB = useRef<LayoutRectangle>(null); const startAni = () => { if (layoutA.current && layoutB.current) { setTimeout(() => { const ani = Animated.timing(aValue, { toValue: layoutB.current.x - layoutA.current.x, duration: 1000, easing: Easing.bezier(0.4, 0, 0.2, 1), useNativeDriver: true, }); ani.start(() => { setTimeout(() => { aValue.setValue(0); }, 1.5 * 1000); }); }, 3 * 1000); } }; const onLayoutB = (event: LayoutChangeEvent) => { layoutB.current = event.nativeEvent.layout; startAni(); }; const onLayoutA = (event: LayoutChangeEvent) => { layoutA.current = event.nativeEvent.layout; startAni(); }; return ( <View style={styles.page}> <Animated.View onLayout={onLayoutA} style={[ styles.A, { transform: [ { translateX: aValue, }, ], }, ]} > <Text style={styles.text}>A</Text> </Animated.View> <View onLayout={onLayoutB} style={styles.B}> <Text style={styles.text}>B</Text> </View> </View> ); }; export default App;
因为A,B谁先layout完成不一定 所以这里需要在两个组件的onLayout方法中都调用动画执行的方法。
注意
异步的
componentDidMount
useEffect
这个节点只能说明vdom构建完成, 并不代表UI渲染完成
因为这个是同一个父组件下的两个元素 所以可以使用x,y直接计算。
如果是跨多个层级的两个元素我们就需要获取到相对屏幕的x,y坐标 而不是相对于各自的父元素。
这个时候我们又该如何做?
measure()是一个异步方法,它在一些场景下可以让你获取元素相对于其父元素或者根视图的位置信息。不同于onLayout 这个api是开发者可以手动调用的,一般在创建的ref上调用。
const App = () => { const aValue = useRef(new Animated.Value(0)).current; const refA = useRef<View>(null); const refB = useRef<View>(null); const layoutA = useRef<{ x: number }>(null); const layoutB = useRef<{ x: number }>(null); const startAni = () => { if (layoutA.current && layoutB.current) { setTimeout(() => { const ani = Animated.timing(aValue, { toValue: layoutB.current.x - layoutA.current.x, duration: 1000, easing: Easing.bezier(0.4, 0, 0.2, 1), useNativeDriver: true, }); ani.start(() => { setTimeout(() => { aValue.setValue(0); }, 1.5 * 1000); }); }, 3 * 1000); } }; const onLayoutB = () => { // x和y:相对于其父元素的位置 // width和height:组件的宽度和高度 // pageX和pageY:相对于设备屏幕的位置 refB.current.measure((x, y, width, height, pageX, pageY) => { layoutB.current = { x: pageX, }; startAni(); }); }; const onLayoutA = () => { refA.current.measure((x, y, width, height, pageX, pageY) => { layoutA.current = { x: pageX, }; startAni(); }); }; return ( <View style={styles.page}> <Animated.View ref={refA} onLayout={onLayoutA} style={[ styles.A, { transform: [ { translateX: aValue, }, ], }, ]} > <Text style={styles.text}>A</Text> </Animated.View> <View ref={refB} onLayout={onLayoutB} style={styles.B}> <Text style={styles.text}>B</Text> </View> </View> ); }; export default App;
通过measure的方法获取pageX, pageY可以获取元素相对于屏幕的位置,然后在计算出相对位移,完成动画。
和onLayout一样measure方法也是异步的 所以我们需要通过回调来获取参数,在调用时机上 一般建议在onLayout中执行,这样可以确保组件已经渲染完成。
measureInWindow也可以获取元素的坐标点,但这个是相对于window的.
window
我们将上面的A的样式修改如下
A: { alignItems: 'center', backgroundColor: 'red', height: 50, justifyContent: 'center', left: 100, position: 'absolute', top: 0, // 这里不设置顶部间距 width: 50, zIndex: 100, },
android沉浸式效果如下:
const onLayoutA = () => { refA.current.measureInWindow((x, y, width, height) => { console.log('test measureInWindow: ', x, y, width, height); }); refA.current.measure((x, y, width, height, pageX, pageY) => { console.log('test measure: ', x, y, width, height, pageX, pageY); }); };
打印结果如下:
可以看到measure的pageX,pageY 为(100,0) measureInWindow的x,y为(100,-32)。 这是因为measureInWindow是相对于window计算的,measure是相对于screen计算的。
ios: screen = window android: screen = status bar + window + 底部Navigation bar(即虚拟按键)
所以我们应该尽量使用screen而不是window。 例如使用Dimensions.get('screen'),不要使用Dimensions.get('window')
The text was updated successfully, but these errors were encountered:
No branches or pull requests
先有问题再有答案
什么场景下需要获取元素的位置信息?
如何获取子元素相对父元素的位置信息
如何获取元素相对屏幕的位置信息
这些方案有什么区别?
如何理解screen 和 window 的区别?
背景
如上demo 这是一个使用React Native构建的动画示例应用。它包含两个视图,一个红色视图(A)和一个蓝色视图(B)。我们需要将A移动到B的位置。
这个时候我们就需要知道A的位置和B的位置 然后通过react native提供的动画api, 在X轴上从其原始位置移动到与B一致的位置 以实现这个功能。
所以我们该如何获取A的位置信息和B的位置信息呢?
方法一:动态样式属性
React Native采用的是CSS in JS的方案。所有的样式都是通过JavaScript的对象来定义的,每个组件的样式都是私有的,可以避免不同组件之间的样式冲突。相同的样式可以被抽象成一个JavaScript对象,然后在不同的组件中重复使用。
由于样式是通过JS来定义的,所以可以根据状态或者属性动态生成样式。
代码如下
由于样式就是一个js对象,我们可以动态的修改A元素在样式对象,因为是开发者设置的绝对布局的定位信息,所以我们可以知道B元素的位置信息。
通过动态样式属性的方法 我们可以准确的拿到在同一个参考体系下的两个元素的相对位置信息。例如A,B 都是绝对定位且都是相对于left设置的偏移。
如果将B的样式改为如下代码
现在我们不能直接知道视图A和视图B之间的距离,因为视图B的位置是相对于屏幕右侧确定的,而视图A的动画位置则是相对于父窗口左侧确定的。 因为在不同的屏幕宽度下 A,B的相对距离是会发生变化的 所以我们不能通过动态样式的方式直接感知到距离是多少。
方案二:onLayout回调
这种情况下我们可以使用react native 提供给我们的onLayout回调方法。当改变屏幕方向或者改变布局的时候,该函数会被触发,并且回传给我们一个包含layout属性的对象。layout属性是一个包含x,y,width,height的对象。
修改代码如下
因为A,B谁先layout完成不一定 所以这里需要在两个组件的onLayout方法中都调用动画执行的方法。
注意
:异步的
,这意味着在组件被渲染后,实际的布局可能需要一些事件才能被计算和提供。所以,不应该认为在componentDidMount
或者useEffect
中可以获取到布局值,这个节点只能说明vdom构建完成, 并不代表UI渲染完成
,渲染UI的行为是由Native进行的。其实不管是react native还是纯web在浏览器环境中 本质上渲染都是异步的。 这也是为什么vue的 nextTick保证在dom完成执行而不保证在渲染完成执行
因为这个是同一个父组件下的两个元素 所以可以使用x,y直接计算。
如果是跨多个层级的两个元素我们就需要获取到相对屏幕的x,y坐标 而不是相对于各自的父元素。
这个时候我们又该如何做?
方案三:measure方法
measure()是一个异步方法,它在一些场景下可以让你获取元素相对于其父元素或者根视图的位置信息。不同于onLayout 这个api是开发者可以手动调用的,一般在创建的ref上调用。
修改代码如下
通过measure的方法获取pageX, pageY可以获取元素相对于屏幕的位置,然后在计算出相对位移,完成动画。
注意
:和onLayout一样measure方法也是异步的 所以我们需要通过回调来获取参数,在调用时机上 一般建议在onLayout中执行,这样可以确保组件已经渲染完成。
方案四:measureInWindow方法
measureInWindow也可以获取元素的坐标点,但这个是相对于
window
的.我们将上面的A的样式修改如下
android沉浸式效果如下:
打印结果如下:
可以看到measure的pageX,pageY 为(100,0)
measureInWindow的x,y为(100,-32)。
这是因为measureInWindow是相对于window计算的,measure是相对于screen计算的。
window & screen的区别
ios: screen = window
android: screen = status bar + window + 底部Navigation bar(即虚拟按键)
所以我们应该尽量使用screen而不是window。
例如使用Dimensions.get('screen'),不要使用
Dimensions.get('window')The text was updated successfully, but these errors were encountered: