react native仿QQ左划

使用PanResponder 和 Animated.timing 完成的一个仿QQ左划出现删除编辑的组件。

1、关键代码

<Animated.View style={{
  flexDirection: "row",
  transform: [{ translateX }],
}}{...PanResponder.create({
  // 要求成为响应者:
  onStartShouldSetPanResponder: (evt, gestureState) => {
    return false;
  },
  onMoveShouldSetPanResponder: (evt, gestureState) => {
    if (Math.abs(gestureState.dx) > 8 && Math.abs(gestureState.dy) < 8) {
      scrollCallBack && scrollCallBack(false);
      return true;
    }
  },
  // onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
  onPanResponderGrant: (evt, gestureState) => {
    // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
    this.handlerSeekGrant(evt, gestureState);
  },
  onPanResponderMove: (evt, gestureState) => {
    // 最近一次的移动距离为gestureState.move{X,Y}
    this.handlerSeekMove(evt, gestureState);
    // 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y}
  },
  onPanResponderTerminationRequest: (evt, gestureState) => {
    if (Platform.OS === "ios") {
      let maxX = this.props.buttonViewWidth;
      let changeX = this.pageXEnd - this.pageXStart;
      let translateX = Math.abs(changeX) > maxX / 4 ? -maxX : 0;
      Animated.timing(this.state.translateX, {
        toValue: translateX,
        duration: 300,
        useNativeDriver: false,
        isInteraction: false,
      }).start();
      if (this.rightBack) this.rightBack = false;
      return true;
    } else {
      return true;
    }
  },
  onPanResponderTerminate: (evt, gestureState) => {
    if (Platform.OS === "android") {
      let maxX = this.props.buttonViewWidth;
      let changeX = this.pageXEnd - this.pageXStart;
      let translateX = Math.abs(changeX) > maxX / 4 ? -maxX : 0;
      Animated.timing(this.state.translateX, {
        toValue: translateX,
        duration: 300,
        useNativeDriver: false,
        isInteraction: false,
      }).start();
      if (this.rightBack) this.rightBack = false;
      return true;
    } else {
      return true;
    }
  },
  onPanResponderRelease: (evt, gestureState) => {
    // 用户放开了所有的触摸点,且此时视图已经成为了响应者。
    // 一般来说这意味着一个手势操作已经成功完成。
    this.handlerSeekRelease(evt, gestureState);
    scrollCallBack && scrollCallBack(true);
  },
}).panHandlers}>

2、handlerSeekGrant方法

handlerSeekGrant(evt, gestureState) {
  this.pageXStart = evt.nativeEvent.pageX;
}

3、handlerSeekMove方法

//手势拖动
handlerSeekMove(evt, gestureState) {
  let x = evt.nativeEvent.pageX;
  let maxX = this.props.buttonViewWidth;
  this.pageXEnd = x;
  let changeX = this.pageXEnd - this.pageXStart;
  let translateX = 0;
  if (changeX < 0) {//左
    if (this.state.translateX.__getValue() === -maxX) {
      translateX = -maxX;
    } else {
      translateX = Math.abs(changeX) > maxX ? -maxX : changeX;
    }
  } else {
    if (this.state.translateX.__getValue() < 0) {
      translateX = Math.abs(-(maxX - changeX)) > maxX ? 0 : -(maxX - changeX);
    } else {
    }
  }
  this.setState({
    translateX: new Animated.Value(translateX),
  }, () => this._eventEmitter());
}

 4、handlerSeekRelease方法

//手势结束
handlerSeekRelease(evt, gestureState) {
  let x = evt.nativeEvent.pageX;
  let maxX = this.props.buttonViewWidth;
  this.pageXEnd = x;
  let changeX = this.pageXEnd - this.pageXStart;
  let translateX = 0;
  if (changeX < 0 || changeX === 0) {//左
    translateX = Math.abs(changeX) > maxX / 4 ? -maxX : 0;
    //如果已经在最做边则往左边拉的时候不做操作
    if (this.state.translateX.__getValue() === -this.props.buttonViewWidth) translateX = -maxX;
  } else if (changeX > 0) {//右
    translateX = Math.abs(changeX) > maxX / 4 ? 0 : -maxX;
  }
  Animated.timing(this.state.translateX, {
    toValue: translateX,
    duration: 300,
    useNativeDriver: false,
    isInteraction: false,
  }).start();
  if (this.rightBack) this.rightBack = false;
}

5、全部代码

import React, { Component } from "react";
import {
  StyleSheet,
  View,
  PanResponder,
  Animated,
  DeviceEventEmitter,
  Platform,
} from "react-native";
import propTypes from "prop-types";


class LeftStrokeComponent extends Component {
  static propTypes = {
    mainChildren: propTypes.element,
    buttonChildren: propTypes.element,
    buttonViewWidth: propTypes.number,
    id: propTypes.any,
    scrollCallBack: propTypes.func,
  };

  constructor(props) {
    super(props);
    this.state = {
      translateX: new Animated.Value(0),
    };
    this.pageXStart = null;
    this.pageXEnd = null;

    this.rightBack = true;//用于判断只要其中一个滚动 其他的就收回

  }

  render() {
    const { translateX } = this.state;
    const { mainChildren, buttonChildren, scrollCallBack } = this.props;
    return (
      <View style={styles.container}>
        <Animated.View style={{
          flexDirection: "row",
          transform: [{ translateX }],
        }}{...PanResponder.create({
          // 要求成为响应者:
          onStartShouldSetPanResponder: (evt, gestureState) => {
            return false;
          },
          onMoveShouldSetPanResponder: (evt, gestureState) => {
            if (Math.abs(gestureState.dx) > 8 && Math.abs(gestureState.dy) < 8) {
              scrollCallBack && scrollCallBack(false);
              return true;
            }
          },
          // onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
          onPanResponderGrant: (evt, gestureState) => {
            // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
            this.handlerSeekGrant(evt, gestureState);
          },
          onPanResponderMove: (evt, gestureState) => {
            // 最近一次的移动距离为gestureState.move{X,Y}
            this.handlerSeekMove(evt, gestureState);
            // 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y}
          },
          onPanResponderTerminationRequest: (evt, gestureState) => {
            if (Platform.OS === "ios") {
              let maxX = this.props.buttonViewWidth;
              let changeX = this.pageXEnd - this.pageXStart;
              let translateX = Math.abs(changeX) > maxX / 4 ? -maxX : 0;
              Animated.timing(this.state.translateX, {
                toValue: translateX,
                duration: 300,
                useNativeDriver: false,
                isInteraction: false,
              }).start();
              if (this.rightBack) this.rightBack = false;
              return true;
            } else {
              return true;
            }
          },
          onPanResponderTerminate: (evt, gestureState) => {
            if (Platform.OS === "android") {
              let maxX = this.props.buttonViewWidth;
              let changeX = this.pageXEnd - this.pageXStart;
              let translateX = Math.abs(changeX) > maxX / 4 ? -maxX : 0;
              Animated.timing(this.state.translateX, {
                toValue: translateX,
                duration: 300,
                useNativeDriver: false,
                isInteraction: false,
              }).start();
              if (this.rightBack) this.rightBack = false;
              return true;
            } else {
              return true;
            }
          },
          onPanResponderRelease: (evt, gestureState) => {
            // 用户放开了所有的触摸点,且此时视图已经成为了响应者。
            // 一般来说这意味着一个手势操作已经成功完成。
            this.handlerSeekRelease(evt, gestureState);
            scrollCallBack && scrollCallBack(true);
          },
        }).panHandlers}>
          {mainChildren}
          {buttonChildren}
        </Animated.View>
      </View>
    );
  }

  //手势开始
  handlerSeekGrant(evt, gestureState) {
    this.pageXStart = evt.nativeEvent.pageX;
  }

  //手势拖动
  handlerSeekMove(evt, gestureState) {
    let x = evt.nativeEvent.pageX;
    let maxX = this.props.buttonViewWidth;
    this.pageXEnd = x;
    let changeX = this.pageXEnd - this.pageXStart;
    let translateX = 0;
    if (changeX < 0) {//左
      if (this.state.translateX.__getValue() === -maxX) {
        translateX = -maxX;
      } else {
        translateX = Math.abs(changeX) > maxX ? -maxX : changeX;
      }
    } else {
      if (this.state.translateX.__getValue() < 0) {
        translateX = Math.abs(-(maxX - changeX)) > maxX ? 0 : -(maxX - changeX);
      } else {
      }
    }
    this.setState({
      translateX: new Animated.Value(translateX),
    }, () => this._eventEmitter());
  }

  //手势结束
  handlerSeekRelease(evt, gestureState) {
    let x = evt.nativeEvent.pageX;
    let maxX = this.props.buttonViewWidth;
    this.pageXEnd = x;
    let changeX = this.pageXEnd - this.pageXStart;
    let translateX = 0;
    if (changeX < 0 || changeX === 0) {//左
      translateX = Math.abs(changeX) > maxX / 4 ? -maxX : 0;
      //如果已经在最做边则往左边拉的时候不做操作
      if (this.state.translateX.__getValue() === -this.props.buttonViewWidth) translateX = -maxX;
    } else if (changeX > 0) {//右
      translateX = Math.abs(changeX) > maxX / 4 ? 0 : -maxX;
    }
    Animated.timing(this.state.translateX, {
      toValue: translateX,
      duration: 300,
      useNativeDriver: false,
      isInteraction: false,
    }).start();
    if (this.rightBack) this.rightBack = false;
  }

  _eventEmitter = () => {
    const { id } = this.props;
    this.eventEmitter = DeviceEventEmitter.emit("LEFTSTROKE", { id });
  };


  _onLeftStoke = (params) => {
    if (!this.rightBack) {
      this.rightBack = true;
      const { id } = params;
      if (id === this.props.id) return;
      if (this.state.translateX >= 0) return;
      Animated.timing(this.state.translateX, {
        toValue: 0,
        duration: 300,
        useNativeDriver: false,
        isInteraction: false,
      }).start();
    }
  };

  componentWillUnmount() {
    this.eventEmitter && this.eventReceive.remove();
    this.eventReceive && this.eventReceive.remove();
  }

  componentDidMount() {
    this.eventReceive = DeviceEventEmitter.addListener("LEFTSTROKE", this._onLeftStoke);
  }
}


export default LeftStrokeComponent;
const styles = StyleSheet.create({
  container: {
    alignItems: "center",
    flexDirection: "row",
    backgroundColor: "white",
  },
});

6、使用方法

<View style={styles.container}>
  <FlatList data={data} renderItem={this._renderItem} onEndReachedThreshold={0.3} scrollEnabled={scrollEnabled} />
</View>
_renderItem = ({ item, index }) => {
  return (
    <LeftStrokeComponent mainChildren={this._renderMainChildren()} buttonChildren={this._renderButtonChildren()}
                         buttonViewWidth={160} id={index}
                         scrollCallBack={(scrollEnabled) => this.setState({ scrollEnabled })} />
  );
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值