ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

reactnative实现qq聊天消息气泡拖拽消失效果

2021-10-29 21:02:45  阅读:222  来源: 互联网

标签:qq const .. state nativeTag evt 拖拽 pan reactnative


前言(可跳过)

我在开发自己的APP时遇到了一个类似于qq聊天消息气泡拖拽消息的需求,因为在网上没有找到相关的组件,所以自己动手实现了一下
需求:对聊天消息气泡拖拽到一定长度松开时该气泡会消失(可自行增加拖拽过程,以及消失的动画)

解决方案:
0.创建一个足够大的数组(长度大于聊天框实例数量),初始化每个元素为React.createRef()
const badgeRegistryArr = new Array(99).fill(React.createRef())

1.渲染聊天列表时,对每个聊天框实例创建一个ref,并映射到数组中(ref={MappingArr[index]})

2.使用PanResponder创建一个触摸手势实例以此来实现拖拽效果(即当onPanResponderMove发生时更改一次Animated的值,已将样式部署到所有实例)

        pan: new Animated.ValueXY(),
    };```
——————

const { pan,currentTargetId } = this.state
const [translateX, translateY] = [pan.x, pan.y];
const imageStyle = {transform: [{translateX}, {translateY}]}


——————
```style={[imageStyle,{position:'relative',zIndex:999}]}```
——————
```onPanResponderGrant: (evt, gestureState) => {
// 设置初始位置
        pan.setValue({x: 0, y: 0});
      },```
——————
```// 使用拖拽的偏移量来定位
onPanResponderMove: Animated.event([
        null, {dx: this.state.pan.x, dy: this.state.pan.y},
      ]),```
3.第2步操作会改变所有聊天气泡的值,所以我们要甄别一下,在开始手势操作事件中我们进行一次setState(得到当前的_nativeTag),然后刷新聊天框实例列表,并将列表中每个元素的_nativeTag和当前_nativeTag进行比对,为true时,将样式部署到且只部署到该实例。这样单独拖拽的效果就实现了
```this.state = {
        pan: new Animated.ValueXY(),
        currentTargetId:0,
    };```

——————
```onPanResponderGrant: (evt, gestureState) => {
        // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
        const { pan } = this.state
        const { _nativeTag } = evt.currentTarget
        this.setState({currentTargetId:_nativeTag})
        pan.setValue({x: 0, y: 0});
      },

——————

    const { pan,currentTargetId } = this.state
    const [translateX, translateY] = [pan.x, pan.y];
    let _nativeTag = null
    _nativeTag = badgeRegistryArr[item.id-1].current ? badgeRegistryArr[item.id-1].current._nativeTag:null
    if(_nativeTag === currentTargetId){
      return {transform: [{translateX}, {translateY}]}
    }
  }

——————
const imageStyle = this.judgeBadgeMove(item);

4.在state中创建一个数组(用来实现拖拽后消失的效果,暂且称为A数组)所有元素初始化为true,数组长度与0步中数组相同,在用户放开触摸点(视为手势操作完成)事件中,当横向或纵向移动长度超出指定值时,对数组A中的匹配元素(目标元素下标即为在第3步中比对操作中的命中元素下标)的值setState为false这样就实现了拖拽一定长度后消失的效果

        pan: new Animated.ValueXY(),
        currentTargetId:0,
        badgeVisualArr:new Array(6).fill(true)
    };```
——————
```onPanResponderRelease: (evt, gestureState) => {
        const { _nativeTag } = evt.currentTarget
        const { dx,dy } = gestureState
        badgeRegistryArr.forEach((value,index) => {
          if(_nativeTag === value.current._nativeTag){
            let tempArr = this.state.badgeVisualArr
            tempArr[index] = false
            this.setState({badgeVisualArr:tempArr})
          }
        })
        if(Math.abs(dx) > 80 || Math.abs(dy)){
          this.setState({badgeVisualValue:false})
        }
        // 用户放开了所有的触摸点,且此时视图已经成为了响应者。
        // 一般来说这意味着一个手势操作已经成功完成。
      },```
——————
```{
            item.messageValue > 0 && badgeVisualArr[item.id-1] ? <Badge 
            value={item.messageValue}
            status='error'
            badgeStyle={{borderColor:'red'}}
          ></Badge>:null
          }```
——————


![](https://www.icode9.com/i/l/?n=20&i=blog/2158262/202110/2158262-20211029202356675-1941541600.gif)

全部代码(以上代码块来自这里,删除了无关代码)
<details>
<summary>点击查看代码</summary>

import React, { Component } from 'react';
import { View, PanResponder,Animated } from 'react-native';
import { Badge} from 'react-native-elements';
import { FlatList } from 'react-native-gesture-handler';

const badgeRegistryArr = new Array(99).fill(React.createRef())

const data = [
{
id:1,
nickName:'wdnmd',
messageValue:0,
face:'../../../assets/images/boy.png',

},{
    id:2,
    nickName:'我带你们打',
    messageValue:10,

    face:'../../../assets/images/boy.png',
      
  },{
    id:3,
  messageValue:0,
    nickName:'wdnmd',
  messageValue:20,
  face:'../../../assets/images/boy.png',
      
  },{
    id:4,
    nickName:'wdnmd',
  messageValue:1,
  face:'../../../assets/images/boy.png',
      
  },{
    id:5,
    nickName:'wdnmd',
  messageValue:0,
  face:'../../../assets/images/boy.png',
      
  },{
    id:6,
    nickName:'wdnmd',
  messageValue:999,
  face:'../../../assets/images/boy.png',
      
  }

]

class Direct extends Component {
constructor(props) {
super(props);
this.state = {
searchBarValue:null,
pan: new Animated.ValueXY(),
currentTargetId:0,
badgeVisualArr:new Array(99).fill(true)
};
}
componentWillMount = function (){
this._panResponder = PanResponder.create({
// 要求成为响应者:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,

  onPanResponderGrant: (evt, gestureState) => {
    // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
    const { pan } = this.state
    const { _nativeTag } = evt.currentTarget
    this.setState({currentTargetId:_nativeTag})
    pan.setValue({x: 0, y: 0});
    // gestureState.{x,y} 现在会被设置为0
  },
  onPanResponderMove: Animated.event([
    null, {dx: this.state.pan.x, dy: this.state.pan.y},
  ]),
  onPanResponderTerminationRequest: (evt, gestureState) => true,
  onPanResponderRelease: (evt, gestureState) => {
    const { _nativeTag } = evt.currentTarget
    const { dx,dy } = gestureState
    badgeRegistryArr.forEach((value,index) => {
      if(_nativeTag === value.current._nativeTag){
        let tempArr = this.state.badgeVisualArr
        tempArr[index] = false
        this.setState({badgeVisualArr:tempArr})
        console.log('binngo')
      }
    })
    if(Math.abs(dx) > 80 || Math.abs(dy)){
      this.setState({badgeVisualValue:false})
    }
    // 用户放开了所有的触摸点,且此时视图已经成为了响应者。
    // 一般来说这意味着一个手势操作已经成功完成。
  },
  onPanResponderTerminate: (evt, gestureState) => {
    // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
  },
  onShouldBlockNativeResponder: (evt, gestureState) => {
    // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
    // 默认返回true。目前暂时只支持android。
    return true;
  },
});

}
judgeBadgeMove = (item) => {
const { pan,currentTargetId } = this.state
const [translateX, translateY] = [pan.x, pan.y];
let _nativeTag = null
_nativeTag = badgeRegistryArr[item.id-1].current ? badgeRegistryArr[item.id-1].current._nativeTag:null
if(_nativeTag === currentTargetId){
return {transform: [{translateX}, {translateY}]}
}
}
renderItem = ({item}) => {
const { badgeVisualArr} = this.state
const imageStyle = this.judgeBadgeMove(item);
return (
<Animated.View
ref={badgeRegistryArr[item.id-1]}
{...this._panResponder.panHandlers}
style={[imageStyle,{position:'relative',zIndex:999}]}
>
{
item.messageValue > 0 && badgeVisualArr[item.id-1] ? <Badge
value={item.messageValue}
status='error'
badgeStyle={{borderColor:'red'}}
>:null
}
</Animated.View>
)
}
render() {
return (



);
}
}

export default Direct;

</details>

标签:qq,const,..,state,nativeTag,evt,拖拽,pan,reactnative
来源: https://www.cnblogs.com/zhaowendao233/p/15483465.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有