记录:RN 实现侧滑删除

记录:RN 实现侧滑删除

这段时间 RN 项目里有个侧滑删除的需求,本文对实现过程做个记录。

代码采用纯 RN 编写,总体来说实现起来还是比较简单的,核心部分不超过 100 行代码。

效果图

效果图

核心代码
import React, {Component} from 'react';
import {Animated, Easing, PanResponder, View, ViewPropTypes} from 'react-native';

type Props = {
  style?: ViewPropTypes.style,
  containerStyle: ViewPropTypes.style,
  // 最大滑动距离
  maxSlide?: number,
};

export default class SlideView extends Component<Props> {

  static defaultProps = {};

  /** 侧滑动画 @private */
  _slideAnim = {
    // 右边的滑动距离
    right: new Animated.Value(0)
  };

  constructor(props) {
    super(props);
    this._initGesture()
  }

  componentDidMount() {
    this._initAnim()
  }

  componentWillUnmount() {
    this._allAnim.forEach(value => value.stop());
  }

  render() {
    const {children, style, containerStyle} = this.props;
    return (
      <View {...this._gesture.panHandlers} style={[{overflow: 'hidden'}, style]}>
        <Animated.View style={[{right: this._slideAnim.right}, containerStyle]}>
          {children}
        </Animated.View>
      </View>
    );
  }

  /** @private */
  _switchSlide() {
    const slideRight = this._slideAnim.right.__getValue();
    if (slideRight !== this.props.maxSlide && slideRight > this.props.maxSlide / 2) {
      // 超过一半,展开
      this._animShow.start();
    } else {
      // 没超过一半,收起
      this._animHide.start();
    }
  }

  /** @private */
  _initAnim() {
    this._allAnim = [
      this._animShow = Animated.timing(
        this._slideAnim.right,
        {
          toValue: this.props.maxSlide,
          duration: 100,
          easing: Easing.in,
        }
      ),
      this._animHide = Animated.timing(
        this._slideAnim.right,
        {
          toValue: 0,
          duration: 100,
          easing: Easing.in,
        }
      ),
    ];
  }

  /** @private */
  _initGesture() {
    let slideRightDistance = 0;
    this._gesture = PanResponder.create({
      onPanResponderTerminationRequest: () => true,
      // 如果 x 轴的位移大于 y 轴位移则需要消费 move 事件
      onMoveShouldSetPanResponder: (e, gs) => Math.abs(gs.dx) > Math.abs(gs.dy),
      // 如果成功申请了事件,就记录当前滑动的距离
      onPanResponderGrant: () => slideRightDistance = this._slideAnim.right.__getValue(),
      onPanResponderMove: (e, gs) => {
        // 当前向左滑动距离
        const slideRight = this._slideAnim.right.__getValue();
        if (gs.dx < 0) {
          // 如果向左滑动距离小于最大滑动值则可以左滑
          this.props.maxSlide > slideRight && this._slideAnim.right.setValue(-gs.dx)
        } else if (slideRight > 0) {
          this._slideAnim.right.setValue(slideRightDistance - gs.dx)
        }
      },
      onPanResponderRelease: () => this._switchSlide(),
      onPanResponderTerminate: () => this._switchSlide(),
    })
  }
}


关于手势处理部分有几个需要注意的点:

  1. 不能申请 down 事件,即 onStartShouldSetPanResponder。若消费了 down 事件会造成外层列表无法滑动等问题。
  2. 在事件被终止时也需要调用 _switchSlide 方法还原 view 状态。
侧滑组件封装
class CollectionItem extends Component {
  render() {
    const {style, contentView, slideMenuWidth, slideMenu} = this.props;
    return (
      <SlideView
        style={style}
        containerStyle={{flex: 1, flexDirection: 'row'}}
        maxSlide={slideMenuWidth}>
        <View style={{width: '100%', padding: 20}}>{contentView}</View>
        {slideMenu}
      </SlideView>
    );
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y-S-J

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值