react 实现浮动可吸附悬浮窗,悬浮球,悬浮按钮,支持拖动拖拽功能(suspend-button)

news2024/11/25 18:39:03

前言:

最近在做移动端,有个需求是 实现一个浮动球可拖拽,能吸附(吸附到 左右两则,距离哪进就吸附到哪边)。
效果图

实现过程:

  1. 使用 suspend-button (但是此组件不支持 ts 和pc端)
npm install suspend-button -S
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import SuspendButton from 'suspend-button'

class App extends Component {
  render() {
    return (
      <SuspendButton></SuspendButton>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('container')
)
属性名类型说明
imgString图片地址
styleobj样式

suspend-button npm地址
这个pc端需要 开启审查,切换到移动端才能看到效果
demo 地址
2. 使用 suspend-button-luckytree (这个支持了ts和 pc、移动都能用)
使用方式和 属性同上

npm i suspend-button-luckytree -S

在这里插入图片描述
suspend-button-luckytree npm地址
demo

3.如果你的定制化需求很大,可以 clone下来代码自己 改造 ,主要代码就是 拖拽吸附 ,他不支持自己传入 一个 ui。只支持一个 img 。所以你可以clone 下来 自己改造一下 。
比如:我自己 需要传入 一个 自定义的dom ,而且 需要传入初始化的位置。
自定义dom 可以利用 props.children,自定义 初始化位置可以 传入style。
代码片段截图

完整代码如下:
index.jsx

import React, { Component } from "react"
import "./index.scss"

class suspendButton extends Component {
  constructor(props) {
    super(props)
    this.state = {
      oLeft: "",
      oTop: ""
    }
    this.$vm = null // 悬浮按钮
    this.moving = false // 移动状态

    this.oW = null // 悬钮距离
    this.oH = null

    this.htmlWidth = null // 页面宽度
    this.htmlHeight = null

    this.bWidth = null // 悬钮宽度
    this.bHeight = null

    this.click = false // 是否是点击
  }

  // 移动触发
  onTouchStart(e) {
    e = e.touches[0]
    this.click = true

    this.oW = e.clientX - this.$vm.getBoundingClientRect().left
    this.oH = e.clientY - this.$vm.getBoundingClientRect().top

    this.htmlWidth = document.documentElement.clientWidth
    this.htmlHeight = document.documentElement.clientHeight

    this.bWidth = this.$vm.offsetWidth
    this.bHeight = this.$vm.offsetHeight

    let oLeft = e.clientX - this.oW
    let oTop = e.clientY - this.oH
    this.setState({
      oLeft,
      oTop
    })

    this.moving = true
  }

  // 移动结束
  onTouchEnd(e) {
    this.moving = false

    this.$vm.className = this.$vm.className + " t-suspend-button-animate"

    // 左侧距离
    let oLeft = this.state.oLeft
    if (oLeft < (this.htmlWidth - this.bWidth) / 2) {
      oLeft = 0
    } else {
      oLeft = this.htmlWidth - this.bWidth
    }

    if (this.click) {
      if (this.props && this.props.onClick) {
        this.props.onClick()
      }
    }
    // }
    // if(oTop < 0) {
    //   oTop = 0
    // } else if (oTop > this.htmlHeight - this.bHeight) {
    //   oTop = this.htmlHeight - this.bHeight
    // }

    this.setState({
      oLeft
      // oTop
    })
  }

  componentDidMount() {
    this.$vm.addEventListener(
      "touchmove",
      e => {
        if (e.cancelable) {
          e.preventDefault()
        }
      },
      {
        passive: false
      }
    )
  }

  // 开始移动
  onTouchMove(e) {
    this.$vm.className = "t-suspend-button"
    this.$vm.bottom="";
    this.$vm.right=""
    this.moving && this.onMove(e)
  }

  // 移动中
  onMove(e) {
    e = e.touches[0]
    this.click = false

    // 左侧距离
    let oLeft = e.clientX - this.oW
    let oTop = e.clientY - this.oH
    if (oLeft < 0) {
      oLeft = 0
    } else if (oLeft > this.htmlWidth - this.bWidth) {
      oLeft = this.htmlWidth - this.bWidth
    }
    if (oTop < 0) {
      oTop = 0
    } else if (oTop > this.htmlHeight - this.bHeight) {
      oTop = this.htmlHeight - this.bHeight
    }

    this.setState({
      oLeft,
      oTop
    })
  }

  render() {
    const { img, style } = this.props
    return (
      <span
        className="t-suspend-button"
        ref={$vm => (this.$vm = $vm)}
        onTouchStart={e => this.onTouchStart(e)}
        onTouchMove={e => this.onTouchMove(e)}
        onTouchEnd={e => this.onTouchEnd(e)}
        style={{
          position:"fixed",
          right:"12px",
          bottom:"100px",
          left: `${this.state.oLeft}px`,
          top: `${this.state.oTop}px`,
          ...style
        }}
      >
        {img ? <img src={img} alt="" /> : this.props.children}
      </span>
    )
  }
}

export default suspendButton


index.scss (css,less都行),我克隆下来 他这个默认的样式不生效

.t-suspend-button {
  //position: fixed;
  //top: 400px;
  //right: 0;
  //width: 4rem;
  //height: 4rem;
  //border-radius: 2rem;
  //box-shadow: 0px 0px 5px rgba(#000000, .4);
}

// .t-suspend-button img {
//   width: 100%;
//   height: 100%;
// }

.t-suspend-button-animate {
  transition-duration: .4s;
}

使用方法:
1.在需要的地方引入组件:
不过具体路径需要根据你自己所放的地方

import SuspendButton from '../components/suspendButton'; 

2.在组件里使用:

<SuspendButton >
        xxx
 </SuspendButton>

属性

属性名类型默认值说明
imgstring图片地址
styleobj{ position:“fixed”,right:“12px”, bottom:“100px”}样式
childrenreactNode自定义的样式和元素

备注:我为什么style 加了默认值呢,是因为我clone下来的代码,放到我的项目里后发现 样式样式不生效。

总结:

我改造的很简单,如果你需要更复杂的效果也可以自己clone下来自己改造。比如:

  1. 动画可以自己传入
  2. 点击后 弹出 一个菜单 菜单里能操作

如果需要支持 pc,移动,ts 则可以选其他版本。甚至可以字及编写一个组件,因为核心代码就是拖拽 这个不限于 react,只要 支持 css语法 基本都能用。vue里把主要代码拿出来,改成vue的语法应该也可以。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/764953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VMWare安装统信UOS虚拟机

单击 创建新的虚拟机 按钮&#xff0c;然后选择 自定义&#xff0c; 然后 下一步 硬件兼容性 选择 Workstation16.x &#xff0c;然后 下一步 选择“稍后安装操作系统”&#xff0c; 然后 下一步 选择 Linux &#xff0c; 再选 版本 CentOS 8 64位/ Ubuntu 均可&#xff0c;然…

【数据结构之树】初阶数据结构之树的实现及其各种方式(上)

文章目录 &#x1f60f;专栏导读&#x1f916;文章导读&#x1f640;树的预备知识&#x1f640;二叉树&#x1f633;树的代码实现及其各类讲解&#x1f332;树的结构体初始化 总结 &#x1f60f;专栏导读 &#x1f47b;作者简介&#xff1a;M malloc&#xff0c;致力于成为嵌入…

LinkNet分割模型搭建

原论文&#xff1a;LinkNet: Exploiting Encoder Representations for Efficient Semantic Segmentation 直接步入正题~~~ 一、LinkNet 1.decoder模块 class DecoderBlock(nn.Module):def __init__(self, in_channels, n_filters): #512, 256super(DecoderBlock, self).__in…

计算机毕业论文选题推荐|软件工程|信息管理|数据分析|系列二

文章目录 导文题目导文 计算机毕业论文选题推荐|软件工程|信息管理|数据分析|系列二 (***语言)==使用其他任何编程语言 例如:基于(***语言)门窗账务管理系统的设计与实现 得到:基于JAVA门窗账务管理系统的设计与实现 基于vue门窗账务管理系统的设计与实现 等等 题目 …

关于21电赛数字识别

这里我们只讲数字识别的代码。 关于模型的训练,这里就不多讲了,看我的这篇文章: K210学习篇(八)在MaixHub训练模型_ODF..的博客-CSDN博客 这里着重讲我们得到训练后的模型该怎么去修改以及和stm32单片机通信。 当我们把下载的模型解压后,就得到一些这些文件,我们只需…

EasyDSS视频直播点播平台如何修改登录密码与开启接口鉴权?

随着互联网的发展&#xff0c;网络安全问题也越来越受到重视。近期我们也对旗下所有的视频平台进行了技术升级&#xff0c;以增强平台的数据安全性&#xff0c;保障用户的信息安全。用户也可以通过以下指导步骤&#xff0c;对平台相关配置进行修改&#xff0c;提高保护等级。 1…

阻塞、挂起和睡眠

挂起&#xff08;主动&#xff09;和阻塞&#xff08;被动&#xff09; 本质&#xff1a;正在执行的进程/线程&#xff0c;由于某些原因主动或者被动的释放 CPU&#xff0c;暂停执行&#xff1b;挂起会将进程移出内存&#xff0c;阻塞的进程还在内存中&#xff1b;挂起时会释放…

CSDN发表文章的常用语法说明

CSDN常用语法说明 一、标题二、文本样式三、列表四、图片五、链接六、目录一级目录二级目录三级目录 七、表格八、注释九、自定义列表十、LaTeX 数学公式十一、插入甘特图十二、插入UML图十三、插入Mermaid流程图十五、插入Flowchart流程图十六、 插入类图十七、快捷键十八、脚…

汽车供应链专场对接会 | 8月25日大会同期活动

爱普搜汽车供应链对接会&#xff0c;是根据采购商的项目需求&#xff0c;有针对性地组织全国各地采购商与供应商&#xff0c;进行面对面交流与沟通&#xff0c;促成实质性交易。参会群体为汽车行业制造型企业、主机厂、Tier1/2。 供应商在参加对接会前已做足功课&#xff0c;现…

使用Linux Deploy搭建服务器(二)使用chroot容器安装linux发行版

一、先下载好软件 Linux Deploy&#xff08;一&#xff09;Linux Deploy简介与软件安装_吻等离子的博客-CSDN博客 二、搭建debian 首先手机要获取root权限 linux Deploy支持许多发行版linux&#xff0c;发行版建议选择Debian&#xff0c;这个版本最好装&#xff0c;Ubuntu …

提升互联网创业项目在搜索结果中的排名的SEO技巧

搜索引擎优化(SEO)技巧&#xff1a;提升互联网创业项目在搜索结果中的排 在当今竞争激烈的互联网创业领域&#xff0c;拥有一个高排名的搜索结果对于项目的成功至关重要。搜索引擎优化&#xff08;SEO&#xff09;是一种有效的策略&#xff0c;可以提高您的互联网创业项目在搜索…

RocketMQ 5.1.0 在java中的使用

版本&#xff1a; 当前测试版本&#xff1a;springBoot 2.3.9、 RocketMQ 5.1.0 Maven或Gradle RocketMQ的依赖项&#xff1a; <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client</artifactId><version>5…

国内开源框架(快速开发,避免重复造轮子)

若依开源框架&#xff08;最容易上手&#xff0c;轻巧简洁&#xff09; 若依开源框架是一款基于SpringBoot2.x和Vue.js的前后端分离的权限管理系统。它采用了前后端分离的架构&#xff0c;使得系统更加灵活、易扩展。同时&#xff0c;它还集成了多种常见的功能模块&#xff0c…

UEC++: 接口

1. 2. 3. 4.一般接口的源文件是不用写逻辑的&#xff0c;一般是在接口头文件中编写 5.被C类继承&#xff1a; 写完函数&#xff0c;千万不允许定义&#xff01;&#xff01;&#xff01; 添加标记宏 找到一个类&#xff1a;继承I开头的接口&#xff1a;引用头文件 错误重写&…

移 动 端

移动端 国内的UC和QQ&#xff0c;百度等手机浏览器都是根据 Webkit 修改过来的内核 兼容移动端主流浏览器 处理 webkit 内核浏览器即可 常见移动端屏幕尺寸 调式 Chrome DevTools&#xff08;谷歌浏览器&#xff09;的模拟手机调试搭建本地 web 服务器&#xff0c; 手机和服…

嵌入式开发--STM32用DMA+IDLE中断方式串口接收不定长数据

回顾 之前讲过用 利用IDLE空闲中断来接收不定长数据 &#xff0c;但是没有用到DMA&#xff0c;其实用DMA会更加的高效&#xff0c;MCU也可以腾出更多的性能去处理应该做的事情。 原理简介 IDLE顾名思义&#xff0c;就是空闲的意思&#xff0c;即当监测到串口空闲超过1个串口…

Java---第五章(类和对象,方法带参)

Java---第五章 一 类和对象类的由来&#xff1a;二者之间的关系this关键字&#xff1a;构造方法 二 方法带参构造方法带参&#xff1a;方法带参对象数组引用数据类型作为方法参数方法重载面向对象说明面向对象和面向过程的区别 一 类和对象 类的由来&#xff1a; 人们在日常生…

【HCIA】11.ACL与NAT地址转换

ACL 通过ACL可以实现对网络中报文流的精确识别和控制&#xff0c;达到控制网络访问行为、防止网络攻击和提高网络带宽利用率的目的。 ACL是由permit或deny语句组成的一系列有顺序的规则的集合&#xff1b;它通过匹配报文的相关字段实现对报文的分类。ACL是能够匹配一个IP数据包…

结合ChatGPT制作PPT

今天看到圈友的一个AI分享&#xff0c;然后自己本身需要做一个分享的PPT。刚好那着帖子实战一下。先说下整体感受。 优点&#xff1a;制作成本确实会比较低&#xff0c;很熟练的话大概就是1分钟一个都有可能。整体流程是先找个第三方PPT制作网站&#xff0c;看下支不支持文本转…

Unity游戏源码分享-Third Person Controller - Shooter Template v1.3.1

Unity游戏源码分享-Third Person Controller - Shooter Template v1.3.1 功能非常齐全 AI格斗 2.5D 完整工程地址&#xff1a;https://download.csdn.net/download/Highning0007/88057824