RN-可拖动的悬浮按钮

RN-可拖动的悬浮按钮

需求

分享悬浮按钮,需要展示在特定页面,并且支持拖动(防止遮挡页面内容)

解决方案思路

1. 封装原生组件,为RN提供原生桥

  • 缺点:两端都需要开发,而且原生组件的位置、大小改变很难适应后续需求、两端代码需要二次封装以对外提供一致的方法和属性。
  • 优点:调用现有组件,不需要额外开发组件相关内容。

2. 第三方组件-react-native-interactable

  • 缺点:需要原生链接一下库,代码比较老(最后维护日期是17年),可能会与当前环境不兼容或出现莫名其妙的bug。
  • 优点:直接集成,瞬间完成开发,节省时间。

3. 参考别人写的案例造轮子-RN开发图标拖动效果实现

  • 缺点:贴的代码太多太长了。。。
  • 优点:。。。

4. 自己搞个RN组件

  • 缺点:完全没思路,不确定多久可以搞定。
  • 优点:组件bug可控,可以通过看文档造轮子学习熟悉一下相关知识。

开整

1. 了解RN中的滑动手势-PanResponder

PanResponder.gif

真是意外发现,官方给出的例子还有代码,已经基本实现了想要的功能。。

首先以当前例子为基础直接贴到我们的demo里慢慢改

运行一下,发现没问题,到目前发现自己搞并不难。所以我们决定更进一步优化,做个松手后自动边缘吸附的效果吧,这样又可以与原生保持一致,而且会有比较好的效果。

2. 通过RN中的动画做吸附效果-Animated.ValueXY

看到这个demo,发现运气太好了已经成功了99%啊。这就是想要的效果啊。

AnimatedValueXY.gif

3. 计算松手后的位置,实现边缘吸附

/*
 * @Author: 毕帅 
 * @Date: 2021-09-03 09:42:36 
 * @Last Modified by: 毕帅
 * @Last Modified time: 2021-09-06 09:45:00
 */
import React, { Component } from "react";
import { Animated, PanResponder } from "react-native";
import { currentScreen } from "../../utils/Utils";

class GJSuspendView extends Component {
  pan = new Animated.ValueXY();
  panResponder = PanResponder.create({
    onMoveShouldSetPanResponder: () => true,
    onPanResponderGrant: () => {
      this.pan.setOffset({
        x: this.pan.x._value,
        y: this.pan.y._value
      });
    },
    onPanResponderMove: Animated.event([
      null,
      { dx: this.pan.x, dy: this.pan.y }
    ]),
    onPanResponderRelease: (e, gestureState) => {
      // 距边界的距离
      let space = 10;
      // 屏幕宽高
      let screenW = currentScreen().width;
      let screenH = currentScreen().height;
      let { locationX, locationY, pageX, pageY } = e.nativeEvent;
      let { dx, dy } = gestureState;
      // locationX locationY 触摸点相对于组件的位置
      // pageX pageY 触摸点相对于根元素也就是屏幕的位置
      // dx dy 拖动了多远

      // 1.计算出最后位置是应该吸附左侧还是右侧
      // 手势结束时组件的x坐标
      let finalX = pageX - locationX;
      let finalY = pageY - locationY;

      let isLeft = screenW / 2 > (finalX + this.viewW / 2);

      // 2.计算最终偏移量
      // 最终x轴偏移量
      let offsetX = 0;
      // 判断应该吸附在左边还是右边
      if (isLeft) {
        offsetX = dx - finalX + space;
      } else {
        offsetX = dx - (finalX + this.viewW + space - screenW);
      }

      let offsetY = dy;
      // 判断view是否超出顶部
      if (finalY < space) {
        offsetY -= (finalY - space);
      }
      // 判断view是否超出底部
      if (finalY + this.viewH + space > screenH) {
        offsetY -= (finalY + this.viewH + space - screenH);
      }
      Animated.spring(
        this.pan, // Auto-multiplexed
        {
          toValue: { x: offsetX, y: offsetY },
          speed: 150,
          bounciness: 0
        }
      ).start(() => {
        console.log('结束');
        this.pan.flattenOffset();
      });
    }
  });

  render() {
    let { renderContentView } = this.props;
    return (
       {
          this.viewW = event.nativeEvent.layout.width;
          this.viewH = event.nativeEvent.layout.height;
          this.viewX = event.nativeEvent.layout.x;
          this.viewY = event.nativeEvent.layout.y;
        }}
        style={{
          transform: [{ translateX: this.pan.x }, { translateY: this.pan.y }]
        }}
        {...this.panResponder.panHandlers}
      >
        {renderContentView()}
      
    );
  }
}

export default GJSuspendView;

4. 总结

我们在官方例子里加上一点四则运算,计算出最终的偏移量,最终达到了想要的效果,有时候官网已经帮我们把代码写好了

RPReplay_Final1630898614.gif

你可能感兴趣的:(RN-可拖动的悬浮按钮)