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
Animated动画库使用方式
布局与定位能力
如何借用ScrollView的滚动能力
PanResponder手势系统
Touchable系列组件
点击事件 & 手势冲突如何解决
完整代码: react-native-radius-view
整体排列如图:
当基于滚动行为做一些逻辑或者动画时 我们首先想到的肯定是基于react native官方提供的已有的组件。在这个场景下是scrollview。那么他是否可以实现我们的需求呢?
scrollview滚动需要满足内容大于视口的条件。这一点我们的场景是不满足的。但是我们可以通过填充空白元素 撑起scrollview的内容 使用组件的滚动能力。然后通过绝对定位 将元素定位到页面的第一屏。可以保证同时响应滚动和点击的能力。
如图所示:
问题:scrollview的内容即使设置了绝对定位 依然会跟随页面滚动。后发现文档里有stickyHeaderIndices属性可以设置不随滚动移动 然鹅... 不支持与属性horizontal={true}一起使用。。。
stickyHeaderIndices
horizontal={true}
在 React Native 中,PanResponder 是用来处理用户手势的系统。它提供了一系列的 API,通过这些 API,你可以定义如何响应用户的拖拽、滑动等手势操作。 核心代码如下:
const panResponder = PanResponder.create({ // 其他东西想要成为响应者。这个视图应该释放响应者吗 onPanResponderTerminationRequest: () => true, // 设置成为响应者 onStartShouldSetPanResponder: () => false, onMoveShouldSetPanResponder(evt, gestureState) { // 取消的情况gestureState数据会被清空 这里保存状态 if (cycleList.length > 1 && Math.abs(gestureState.dx) > 15) { panDx.current = { ...gestureState }; return true; } return false; }, onStartShouldSetPanResponderCapture: () => false, // 抬起 onPanResponderRelease(evt, gestureState) { onTouchEnd(gestureState); }, // 取消 onPanResponderTerminate(evt, gestureState) { onTouchEnd(panDx.current || gestureState); }, });
每个 PanResponder 回调中都会接受到一个 gestureState 对象,它有以下关键属性:
如果手势被取消 当onPanResponderTerminate被执行时 gestureState对象会被清空 因此如果想基于gestureState在onPanResponderTerminate中执行逻辑时 需要手动保存下这个对象。
通过设置开始不响应并且在滚动时拦截 可以解决点击和手势事件冲突的问题。
onStartShouldSetPanResponder: () => false, onMoveShouldSetPanResponder(evt, gestureState) { if (cycleList.length > 1 && Math.abs(gestureState.dx) > 15) { panDx.current = { ...gestureState }; return true; } return false; },
滚动动画的本质是从屏幕的(x,y)移动到另一个(x,y)。那么我们如何获取到对应的坐标呢?
我们可以通过onLayout获取每个视图的坐标点,并基于此计算出视图的中心坐标。然后保存在ref中。
onLayout
const layoutListRef = useRef<LayoutItem[]>([]); const onItemLayout = (e, index) => { layoutListRef.current[index] = { centerX: e.nativeEvent.layout.x + e.nativeEvent.layout.width / 2, centerY: e.nativeEvent.layout.y + e.nativeEvent.layout.height / 2, }; };
<Animated.View style={stylesList} onLayout={e => onItemLayout(e, index)} key={index} > <TouchableWithoutFeedback onPress={() => onClick(index)}> {renderItem(data, ani, index)} </TouchableWithoutFeedback> </Animated.View>
因为数组长度为【1,9】我们可以通过这个长度计算出【start, end】. 例如长度为4的情况start=3 end=6 center=4(中心位置永远不变)。
【start, end】形成一个窗口 当左右滑动时 窗口左右移动 当左右窗口边界位于中心点时即到达滚动边缘 不可以再继续向对应方向滚动。
对于边界也要做相应的处理 当已经处于边界外围并且移动的下一步依然在边界外面 我们不需要移动当前视图。
先看下最基本的Animated动画是如何实现的
import React, { useEffect, useRef } from 'react'; import { Animated, Text, View } from 'react-native'; const FadeInView = (props) => { const fadeAnim = useRef(new Animated.Value(0)).current; // 1:初始化动画属性 useEffect(() => { // 3:通过api开始动画属性的变化 Animated.timing( fadeAnim, { toValue: 1, // 目标透明度 duration: 5000, // 动画时长 useNativeDriver: true, // 启用原生动画驱动 } ).start(); }, [fadeAnim]) return ( <Animated.View // 使用 Animated.View style={{ ...props.style, opacity: fadeAnim, // 将 动画属性和动画组件的样式关联 }} > {props.children} </Animated.View> ); } // 使用 <FadeInView style={{width: 200, height: 50, backgroundColor: 'powderblue'}}> <Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text> </FadeInView>
使用 Animated 库通常分为以下几个步骤:
需要将Animated动画值声明为不可变.
const fadeAnim = new Animated.Value(0);
在这种情况下,每当组件重新渲染时,fadeAnim 将被重新实例化,从而导致:
这种做法不仅影响动画的正常执行,还可能引入性能问题,因为频繁的实例化和垃圾回收会消耗额外的资源。
const leftXY = (step = 1) => { const aniListXY = []; [...infoList.current].reverse().forEach((info, i) => { let s = 1; let o = 0; const item = cycleList[info.pos]; let index = end - i; // 当前的元素位置 if (index < 0) { return; } const nextIndex = Math.max(index - step, 0); // 将要移动到的下一个元素位置 if (index >= LocationNums.length && nextIndex >= LocationNums.length) { // 处理超出边界的case return; } index = Math.min(end - i, LocationNums.length - 1); const x = layoutListRef.current[LocationNums[nextIndex]].centerX - layoutListRef.current[LocationNums[index]].centerX; const y = layoutListRef.current[LocationNums[nextIndex]].centerY - layoutListRef.current[LocationNums[index]].centerY; if (LocationNums[nextIndex] === 0) { s = 1.5; o = 1; } info.offsetX = info.offsetX + x; info.offsetY = info.offsetY + y; generateAniList(aniListXY, item, { x: info.offsetX, y: info.offsetY, s, o }); }); changeCenterIndex(true, step); setStart(start - step); setEnd(end - step); return aniListXY; };
The text was updated successfully, but these errors were encountered:
No branches or pull requests
你将掌握的知识点:
Animated动画库使用方式
布局与定位能力
如何借用ScrollView的滚动能力
PanResponder手势系统
Touchable系列组件
点击事件 & 手势冲突如何解决
效果展示:
完整代码:
react-native-radius-view
功能分析
整体排列如图:
需要解决的问题
例如用户向左滑动一次 0->2的位置 2->4 4->6 6->8 8已经在最边缘了保持不变。右侧同理 依次滚动到前面的空位。
问题1:如何实现点击和手势滑动
通过Touchable & scrollview组合
当基于滚动行为做一些逻辑或者动画时 我们首先想到的肯定是基于react native官方提供的已有的组件。在这个场景下是scrollview。那么他是否可以实现我们的需求呢?
scrollview滚动需要满足内容大于视口的条件。这一点我们的场景是不满足的。但是我们可以通过填充空白元素 撑起scrollview的内容 使用组件的滚动能力。然后通过绝对定位 将元素定位到页面的第一屏。可以保证同时响应滚动和点击的能力。
如图所示:
问题:scrollview的内容即使设置了绝对定位 依然会跟随页面滚动。后发现文档里有
stickyHeaderIndices
属性可以设置不随滚动移动 然鹅... 不支持与属性horizontal={true}
一起使用。。。通过Touchable & PanResponder一起使用
PanResponder
在 React Native 中,PanResponder 是用来处理用户手势的系统。它提供了一系列的 API,通过这些 API,你可以定义如何响应用户的拖拽、滑动等手势操作。
核心代码如下:
gestureState 对象
每个 PanResponder 回调中都会接受到一个 gestureState 对象,它有以下关键属性:
注意
如果手势被取消 当onPanResponderTerminate被执行时 gestureState对象会被清空 因此如果想基于gestureState在onPanResponderTerminate中执行逻辑时 需要手动保存下这个对象。
点击与手势冲突
通过设置开始不响应并且在滚动时拦截 可以解决点击和手势事件冲突的问题。
问题2:如何确定将要滚动到那个位置
滚动动画的本质是从屏幕的(x,y)移动到另一个(x,y)。那么我们如何获取到对应的坐标呢?
我们可以通过
onLayout
获取每个视图的坐标点,并基于此计算出视图的中心坐标。然后保存在ref中。问题3:左右滑动的边界在哪里
因为数组长度为【1,9】我们可以通过这个长度计算出【start, end】.
例如长度为4的情况start=3 end=6 center=4(中心位置永远不变)。
【start, end】形成一个窗口 当左右滑动时 窗口左右移动
当左右窗口边界位于中心点时即到达滚动边缘 不可以再继续向对应方向滚动。
对于边界也要做相应的处理 当已经处于边界外围并且移动的下一步依然在边界外面 我们不需要移动当前视图。
问题4:滚动的动画如何实现
先看下最基本的Animated动画是如何实现的
使用 Animated 库通常分为以下几个步骤:
注意
需要将Animated动画值声明为不可变.
在这种情况下,每当组件重新渲染时,fadeAnim 将被重新实例化,从而导致:
这种做法不仅影响动画的正常执行,还可能引入性能问题,因为频繁的实例化和垃圾回收会消耗额外的资源。
具体实现
参考
The text was updated successfully, but these errors were encountered: