Animated模仿淘宝购物车侧滑删除

·  阅读 391
Animated模仿淘宝购物车侧滑删除

这是我参与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;
复制代码

结语

以上就是一个简单的侧滑删除动画。

如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。

文章如有错误之处,希望在评论区指正🙏🙏。

分类:
前端
标签: