vue实现移动端悬浮可拖拽按钮

news2024/11/25 2:55:57

需求:

  1. 按钮在页面侧边悬浮显示;
  2. 点击按钮可展开多个快捷方式按钮,从下向上展开。
  3. 长按按钮,则允许拖拽来改变按钮位置,按钮为非展开状态;
  4. 按钮移动结束,手指松开,计算距离左右两侧距离并自动移动至侧边显示;
  5. 移动至侧边后,根据具体左右两次位置判断改变展开样式;
  6. 处理移动到非可视区域时特殊情况。

效果展示:
在这里插入图片描述

具体实现:

<template>
  <div class="shortcut" @touchstart="touchstart($event)" @touchmove="touchMove($event)" @touchend="touchEnd($event)" v-if="isMobile">
    <div class="shortcut__container">
      <transition name="fade">
        <div class="shadow" v-if="showPopover" @click.stop="showPopover = false"></div>
      </transition>
      <transition name="sub-fade">
        <div :class="['shortcut__list', `${type}`]" v-if="showPopover">
          <div class="shortcut__list_item">
            <div class="icon-box"><img src="@/images/common/ic_question.png" alt=""></div>
            投资理财
          </div>
          <div class="shortcut__list_item">
            <div class="icon-box"><img src="@/images/common/ic_question.png" alt=""></div>
            我的资产
          </div>
          <div class="shortcut__list_item">
            <div class="icon-box"><img src="@/images/common/ic_question.png" alt=""></div>
            咨询我们
          </div>
        </div>
      </transition>
      <div class="shortcut__btn" :class="{ anim: showPopover }" @click.stop="handleBtn()">+</div>
    </div>
  </div>
</template>

<script>
const TIME = 50
export default {
  data () {
    return {
      isMobile: /Mobi|Android|iPhone/i.test(navigator.userAgent),
      showPopover: false,
      timeOutEvent: 0,
      longClick: 0,
      // 手指原始位置
      oldMousePos: {},
      // 元素原始位置
      oldNodePos: {},
      type: 'right',
    }
  },
  methods: {
    touchstart (ev) {
      // 定时器控制长按时间,超过{TIME}毫秒开始进行拖拽
      this.timeOutEvent = setTimeout(() => {
        this.longClick = 1
      }, TIME)
      const selectDom = ev.currentTarget
      const { pageX, pageY } = ev.touches[0] // 手指位置
      const { offsetLeft, offsetTop } = selectDom // 元素位置
      // 手指原始位置
      this.oldMousePos = {
        x: pageX,
        y: pageY,
      }
      // 元素原始位置
      this.oldNodePos = {
        x: offsetLeft,
        y: offsetTop,
      }
      this.handleMoving()
      selectDom.style.left = `${offsetLeft}px`
      selectDom.style.top = `${offsetTop}px`
    },
    touchMove (ev) {
      // 未达到{TIME}毫秒就移动则不触发长按,清空定时器
      clearTimeout(this.timeOutEvent)
      if (this.longClick === 1) {
        this.handleMoving()
        this.showPopover = false

        const selectDom = ev.currentTarget
        // x轴偏移量
        const lefts = this.oldMousePos.x - this.oldNodePos.x
        // y轴偏移量
        const tops = this.oldMousePos.y - this.oldNodePos.y
        const { pageX, pageY } = ev.touches[0] // 手指位置
        selectDom.style.left = `${pageX - lefts}px`
        selectDom.style.top = `${pageY - tops}px`
      }
    },
    touchEnd (ev) {
      // 清空定时器
      clearTimeout(this.timeOutEvent)
      if (this.longClick === 1) {
        this.longClick = 0
        const selectDom = ev.currentTarget
        const { innerWidth, innerHeight } = window
        const { offsetLeft, offsetTop } = selectDom
        selectDom.style.left = offsetLeft + 50 > innerWidth / 2 ? 'calc(100% - 55px)' : '15px'
        if (offsetTop < 150) {
          selectDom.style.top = '150px'
        } else if (offsetTop + 150 > innerHeight) {
          selectDom.style.top = `${innerHeight - 150}px`
        }
        this.type = offsetLeft + 50 > innerWidth / 2 ? 'right' : 'left'

        setTimeout(() => {
          document.body.style.overflow = 'auto'
          document.body.style.userSelect = 'auto'
        }, 1000)
      }
    },
    handleMoving () {
      // 禁止body滚动
      document.body.style.overflow = 'hidden'
      // 禁止body文本选中
      document.body.style.userSelect = 'none'
    },
    handleBtn () {
      this.showPopover = !this.showPopover
    }
  },
}
</script>

<style scoped lang="less">
.icon-box {
  background: #fff;
  width: .8rem;
  height: .8rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.shortcut {
  position: fixed;
  z-index: 9999;
  left: calc(100% - 55px);
  top: calc(100% - 150px);
  user-select: none;

  &__container {
    position: relative;
  }

  &__list {
    position: absolute;
    bottom: .8rem;
    z-index: 8;
    &_item {
      color: #fff;
      display: flex;
      flex-direction: row;
      align-items: center;
      white-space: nowrap;
      margin-bottom: .15rem;

      .icon-box {
        margin: 0 .1rem 0 0;

        img {
          width: 0.36rem;
          height: 0.36rem;
        }
      }
    }

    &.left {
      left: 0;
    }

    &.right {
      right: 0;
      .shortcut__list_item {
        flex-direction: row-reverse;
        .icon-box {
          margin: 0 0 0 .1rem;
        }
      }
    }
  }

  &__btn {
    background: #fff;
    width: .8rem;
    height: .8rem;
    border-radius: 50%;
    text-align: center;
    line-height: .7rem;
    color: #3356D9;
    font-size: .5rem;
    position: relative;
    z-index: 8;
    border: 1px solid #3356D9;
    transition: all .3s linear;

    &.anim {
      transform: rotate(135deg);
    }
  }
}
.shadow {
  width: 100%;
  max-width: 1024px;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 1;
  margin: 0 auto;
}
.sub-fade-leave-active,.sub-fade-enter-active {
  transition: max-height 0.3s linear;
}
.sub-fade-enter,.sub-fade-leave-to {
  max-height: 0;
  overflow: hidden;
}
.sub-fade-enter-to,.sub-fade-leave {
  max-height: 2.56rem;
  overflow: hidden;
}
</style>

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

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

相关文章

喜迎中秋国庆双节,华为云Astro Canvas之我的中秋节设计大屏

目录 前言 前提条件 作品展示 薅羊毛 前言 大屏应用华为云Astro Canvas是华为云低代码平台Astro的子服务之一&#xff0c;是以数据可视化为核心&#xff0c;以屏幕轻松编排&#xff0c;多屏适配可视为基础&#xff0c;用户可通过图形化界面轻松搭建专业水准的数据可视化大屏…

面试:Spring中单例模式用的是哪种?

你好&#xff0c;我是田哥 需要简历优化、模拟面试、面试辅导、技术辅导......请联系我。10年码农24小时在线为你服务。 面试中被问到设计模式的概率还是蛮高的&#xff0c;尤其是问&#xff1a;你在项目中用过设计模式吗&#xff1f; 面对这个问题&#xff0c;我也在做模拟面试…

使用香橙派 在Linux环境中安装并学习Python

前言 在实际项目中&#xff0c;经常会遇到需要使用人工智能的场景&#xff0c;如人脸识别&#xff0c;车牌识别等...其一般的流程就是由单片机采集数据发送给提供人工智能算法模型的公司&#xff08;百度云&#xff0c;阿里云...&#xff09;&#xff0c;然后人工智能将结果回…

使用 Python 函数callable和isinstance的意义

一、说明 在这篇博客中&#xff0c;我们将探讨两个python函数&#xff1a;1 callable 中的函数及其有趣的应用程序。该callable函数用于检查对象是否可调用&#xff0c;这意味着它可以作为函数调用。2 isinstance这个内置函数允许我们比较两种不同的数据类型并确定它们是否相…

rancher部署pv、pvc、离线部署nfs

&#xff08;1&#xff09;NFS离线安装 使用nfs配置两台机器共享目录 假设两台机器188.188.30.32&#xff08;服务端&#xff09;、188.188.30.31&#xff08;客户端&#xff09;配置nfs 1.在可以联网的机器上下载rpm安装包 yum -y install nfs-utils --downloadonly --dow…

ETF场内基金佣金最低可达万0.5!速速办理

2023年ETF基金开户&#xff0c;交易手续费佣金一般默认是万三左右的&#xff0c;最低是5元起。想要申请低佣金是可以通过线上客户经理办理的&#xff0c;客户经理手中一般都是有低佣金开户渠道的&#xff0c;账户开通后还可以给您提供VIP专属服务&#xff0c;十几分钟就可以办理…

NPU上PyTorch模型训练问题案例

在昇腾AI处理器上训练PyTorch框架模型时&#xff0c;可能由于环境变量设置问题、训练脚本代码问题&#xff0c;导致打印出的堆栈报错与实际错误并不一致、脚本运行异常等问题&#xff0c;那么本期就分享几个关于PyTorch模型训练问题的典型案例&#xff0c;并给出原因分析及解决…

动态照片怎么制作?教你如何制作gif动图

Gif动图想必大家都不陌生吧&#xff01;那么&#xff0c;这种gif格式的动图要怎么操作呢&#xff1f;很简单通过使用gif动态图片制作&#xff08;https://www.gif.cn/&#xff09;工具-GIF中文网&#xff0c;只需上传jpg、png格式的两张以上图片无需下载软件&#xff0c;手机、…

为什么说网络安全是IT行业最后的红利?是风口行业?

前言 “没有网络安全就没有国家安全”。当前&#xff0c;网络安全已被提升到国家战略的高度&#xff0c;成为影响国家安全、社会稳定至关重要的因素之一。 网络安全行业特点 1、就业薪资非常高&#xff0c;涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万…

uniapp ui安装 阿里图标库使用 报错 Assignment to constant variable.

安装 ui uni-app官网 (dcloud.net.cn) &#xff08;一&#xff09;安装 pages.js配置 安装 sassnpm i sass -D 或 yarn add sass -D 安装 sass-loader npm i sass-loader10.1.1 -D 或 yarn add sass-loader10.1.1 -D安装 uni-uinpm i dcloudio/uni-ui 或 yarn a…

VMware:一个多云+AI的未来

“以往在应用人工智能时&#xff0c;首先你需要一个基础算法模型&#xff0c;然后使用特定的数据进行处理&#xff0c;最后再将其加人到应用程序上…… 但是很显然&#xff0c;这里的每一步骤都涉及到法律和隐私问题&#xff1a;算法模型的知识产权、私人数据的法律风险&#x…

计算机竞赛 深度学习人体跌倒检测 -yolo 机器视觉 opencv python

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的人体跌倒检测算法研究与实现 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满…

解决VUE报错GET http://127.0.0.1:5500/favicon.ico 404 (Not Found)

当我们启动VUE项目的时候控制台会报错错误GET http://127.0.0.1:5500/favicon.ico 404 (Not Found) 原因就是项目下的 favicon.ico 找不见&#xff0c;缺少一个页签图标 解决方法&#xff1a; 在根目录下放置一张图片&#xff0c;重命名为favicon.ico 这个时候选中的图片就会…

算法-版本号升级

背景 今天看到了一个工具类AutoUpgradeVersionUtil&#xff0c;觉得很有意思&#xff0c;大体的作用是版本号的升级&#xff0c;类似于从0.0.1-> 0.0.2这样的。我一想&#xff0c;我之前刷算法的时候这样的案例遇到的多着呢&#xff0c;还有很多种的变种&#xff01; 数字…

云原生Kubernetes:K8S安全机制

目录 一、理论 1.K8S安全机制 2.Authentication认证 3.Authorization授权 4.Admission Control准入控制 5.User访问案例 6.ServiceAccount访问案例 二、实验 1.Admission Control准入控制 2.User访问案例 3.ServiceAccount访问案例 三、问题 1.生成资源报错 2.镜…

IDM中下载请求两次无法下载的解决办法

IDM中下载请求两次无法下载的解决办法 遇到的问题描述solution分析原因 遇到的问题描述 如果你在其他地方的帖子无法解决&#xff0c;试试我的包治百病&#xff01; 本人的idm版本老一点&#xff0c;很久没更新。在下载很多文件的时候会出现两次重复下载的情况&#xff0c;今天…

【设计模式】四、工厂模式

文章目录 概述工厂模式简单工厂模式&#xff1a;工厂方法模式抽象工厂模式小结 概述工厂模式 传统方式&#xff1a; 简单工厂模式&#xff1a; 简单工厂模式的设计方案: 定义一个可以实例化 Pizaa 对象的类&#xff0c;封装创建对象的代码。 存在的问题&#xff1a; 简单工厂…

微信小程序 语法学习

1. 注册小程序账号 https://mp.weixin.qq.com/cgi-bin/wx 2. 获取appId 开发管理 -> 开发设置 3. 下载开发工具 https://developers.weixin.qq.com/miniprogram/dev/devtools/devtools.html 4. 登录开发工具 不推荐游客模式&#xff0c;使用微信扫描 工具内置了很多模…

25835-2010 缆索用环氧涂层钢丝 阅读笔记

声明 本文是学习GB-T 25835-2010 缆索用环氧涂层钢丝. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了缆索用环氧涂层钢丝的术语和定义、产品标记、订货内容、材料、涂覆、技术要求、涂层 的修补、试验方法、检验规则、包装、标…

转载-C#学习笔记-基本概念(CLR、CTS、CLS...)

1. CLR(Common Language Runtime&#xff0c;公共语言运行时(库)) 可由多种.NET语言使用的运行时环境&#xff0c;其主要作用是定位、加载和管理.NET类型、内存管理、安全检查、线程管理等。.NET运行库提供了一个定义明确的运行库层&#xff0c;可以被支持.NET的所有语言和平台…