这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战
前言
“八月的色彩是用金子铸就的,明亮而珍贵;八月的色彩是用阳光酿造的,芬芳而灿烂。”
未来的日子,愿你把自己调至最佳状态,缓缓努力,慢慢变好 Y(^o^)Y
本文接上篇 RN 的 Animated。
今天我们使用 Animated 来实现一个简单的购物车侧滑删除功能(模仿淘宝)。
本文所涉及都是使用 函数式组件
先上效果图
了解 PanResponder
对于React Native来说,点击事件(触摸)是很容易的,但是要实现滑动删除功能,就会涉及到触摸的手势。同样React Native本身也为开发者准备了手势想过的api——PanResponder。
PanResponder类可以将多点触摸操作协调成一个手势。它使得一个单点触摸可以接受更多的触摸操作,也可以用于识别简单的多点触摸手势。
它提供了一个对触摸响应系统响应器的可预测的包装。对于每一个处理函数,它在原生事件之外提供了一个新的gestureState对象:
onPanResponderMove: (event, gestureState) => {}
一个gestureState对象有如下的字段:
stateID- 触摸状态的ID。在屏幕上有至少一个触摸点的情况下,这个ID会一直有效。moveX- 最近一次移动时的屏幕横坐标moveY- 最近一次移动时的屏幕纵坐标x0- 当响应器产生时的屏幕坐标y0- 当响应器产生时的屏幕坐标dx- 从触摸操作开始时的累计横向路程dy- 从触摸操作开始时的累计纵向路程vx- 当前的横向移动速度vy- 当前的纵向移动速度numberActiveTouches- 当前在屏幕上的有效触摸点的数量
创建 PanResponder 对象
首先我们需要创建一个PanResponder对象,同事设置它为响应者
PanResponder.create({
// 响应触摸事件,如果返回false,则无效
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
})
设置 Animated
上一篇文章,我们已经介绍了 Animated 是如何使用的。这里不再赘述
初始化创建驱动动画的值
const pan = useRef(new Animated.Value(0)).current;
执行动画,这里使用Animated.spring()弹性模型。
/**
* @description: 执行动画修改 pan 的值
* @param {number} num
*/
const _startAnimated = num => {
Animated.spring(pan, {
toValue: num, // 设置动画的属性值
useNativeDriver: true,
}).start();
};
触摸事件执行动画
在滑动时候,我们去获取它的 gestureState.dx,通过判断 gestureState.dx 的正负值情况来确定滑动的方向。
gestureState.dx> 0 向右滑动gestureState.dx< 0 向左滑动
确定方向后,我们设置Animated.View 动画属性的值 transform: [{translateX: pan}],
// 触摸滑动
onPanResponderMove: (event, gestureState) => {
console.log('move: ', gestureState.dx);
// 向右滑动
if (gestureState.dx > 0) {
_startAnimated(0);
} else if(gestureState.dx <= -50) {
// 向左滑动
_startAnimated(-150);
}
}
完整代码
import React, {useRef, useState, useCallback} from 'react';
import {
Animated,
View,
StyleSheet,
PanResponder,
Text,
TouchableOpacity,
Alert,
} from 'react-native';
const WIDTH = 50;
const SwipeAction = () => {
const [contentWidth, setContentWidth] = useState(0);
const pan = useRef(new Animated.Value(0)).current;
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: (evt, gestureState) => {
// 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
console.log('start');
},
onPanResponderMove: (event, gestureState) => {
console.log('move: ', gestureState.dx);
// 向右滑动
if (gestureState.dx > 0) {
_startAnimated(0);
} else if(gestureState.dx <= - WIDTH) {
// 向左滑动
_startAnimated(- WIDTH * 3);
}
},
// onPanResponderRelease: (evt, gestureState) => {
// },
}),
).current;
const onLayout = ({
nativeEvent: {
layout: {width},
},
}) => {
// 获取内容的宽度
setContentWidth(width);
};
/**
* @description: 执行动画修改 pan 的值
* @param {number} num
*/
const _startAnimated = num => {
Animated.spring(pan, {
toValue: num,
useNativeDriver: true,
}).start();
};
const handleDelete = () => {
Alert.alert('Alert Title', '确定删除吗?', [
{
text: 'Cancel',
onPress: () => _startAnimated(0),
style: 'cancel',
},
{
text: 'OK',
onPress: () => _startAnimated(0),
},
]);
};
return (
<View style={styles.container} onLayout={onLayout}>
<TouchableOpacity>
<Text style={[styles.text, styles.search]}>找相似</Text>
</TouchableOpacity>
<TouchableOpacity>
<View style={styles.insertCollect}>
<Text style={styles.text}>移入</Text>
<Text style={styles.text}>收藏夹</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={handleDelete}>
<Text style={[styles.text, styles.delete]}>删除</Text>
</TouchableOpacity>
<Animated.View
style={[
styles.content,
{
width: contentWidth,
transform: [{translateX: pan}],
},
]}
{...panResponder.panHandlers}>
<View>
<Text style={styles.box}>这是一个描述性内容啦啦啦啦啦啦啦啦啦啦啦</Text>
</View>
</Animated.View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
marginTop: 100,
backgroundColor: '#FFF',
justifyContent: 'flex-end',
},
titleText: {
fontSize: 14,
lineHeight: 24,
fontWeight: 'bold',
},
content: {
position: 'absolute',
backgroundColor: '#fff',
},
box: {
height: 80,
lineHeight: 80,
borderRadius: 0,
textAlign: 'center',
color: 'black',
},
delete: {
backgroundColor: '#DF3409',
height: 80,
lineHeight: 80,
width: WIDTH,
textAlign: 'center',
},
insertCollect: {
backgroundColor: '#FF6347',
height: 80,
lineHeight: 80,
width: WIDTH,
justifyContent: 'center',
alignItems: 'center',
},
search: {
backgroundColor: '#FFBA30',
height: 80,
lineHeight: 80,
width: WIDTH,
textAlign: 'center',
},
text: {
color: '#fff',
},
});
export default SwipeAction;
结语
以上就是一个简单的侧滑删除动画。
如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏。