Vue移动端悬浮框显示,支持拖拽及点击传递

发布于:2025-08-02 ⋅ 阅读:(17) ⋅ 点赞:(0)

前言

最近产品提了一个需求,让移动端项目的所有页面(除了登录、个人中心不展示),新增一个悬浮框,并且支持在可视区域随意拖拽,点击可以跳转到指定页面。

最终效果

在这里插入图片描述

组件封装的参数配置

1、代码示例:

<t-floating-box :visible="visible" @click="jumpPage"/>

2、配置参数(t-floating-box Attributes)

参数 说明 类型 默认值
visible 是否显示悬浮框 Boolean true
width 悬浮框宽 number 40 px
height 悬浮框宽高 number 40 px
name 悬浮框文案 String -
zIndex 悬浮框z-index number 999

3、events 事件

事件名称 说明 回调参数
click 点击悬浮框事件 -

源码

<template>
  <div
      v-show="visible"
      class="t_floating_box"
      @click="handleClick"
      :style="{top: typeof top === 'number' ? `${top}px` : top, left: typeof left === 'number' ? `${left}px` : left, width: `${width}px`, height: `${height}px`, zIndex: zIndex}"
      ref="move_div"
      @touchstart="down"
      @touchmove="move"
      @touchend="end"
    >{{name}}</div>
</template>

<script>
export default {
  name: "TFloatingBox",
  props: {
     visible: {
      type: Boolean,
      default: true
    },
    width: {
      type: [String, Number],
      default: 40
    },
    height: {
      type: [String, Number],
      default: 40
    },
    name: {
      type: String,
      default: ''
    },
    zIndex: {
      type: Number,
      default: 999
    }
  },
  data() {
    return {
      flags: false, // 鼠标按下标志
      position: { x: 0, y: 0, left: 320, top: 630 },
      top: 630,
      left: 320,
      width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
      height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,     
    }
  },

  mounted() {
    // 初始化
    // this.top = window.innerHeight - 80
    // this.left = window.innerWidth - 50
    this.top = '85%'
    this.left = '86%'
    this.position.top = this.top
    this.position.left = this.left
  },
  methods: {
    handleClick() {
      this.$emit('click')
    },
    // 拖动开始的操作
    down() {
      this.flags = true
      const refs = this.$refs.move_div?.getBoundingClientRect()
      let touch = event
      if (event.touches) {
        touch = event.touches[0]
      }
      this.position.x = touch.clientX
      this.position.y = touch.clientY
      this.position.left = refs.left
      this.position.top = refs.top
    },
    // 拖动中的操作
    move() {
      if (this.flags) {
        let touch = event
        if (event.touches) {
          touch = event.touches[0]
        }
        const xPum = this.position.left + touch.clientX - this.position.x
        const yPum = this.position.top + touch.clientY - this.position.y
        this.left = xPum
        this.top = yPum
        this.banOut()
        // 阻止页面的滑动默认事件
        // document.addEventListener('touchmove', function () {
        //   event.preventDefault()
        // }, { passive: false })
      }
    },
    // 拖动结束的操作
    end() {
      this.flags = false
      this.banOut()
    },
    // 避免拖动出界的限制
    banOut() {
      const refs = this.$refs.move_div?.getBoundingClientRect()
      if (this.left < 0) {
        this.left = 0
      } else if (this.left > this.width - refs.width) {
        this.left = this.width - refs.width
      }
      if (this.top < 0) {
        this.top = 0
      } else if (this.top > this.height - refs.height) {
        this.top = this.height - refs.height
      }
    },
  }
}
</script>

<style lang="scss">
  .t_floating_box {
    position: fixed;
    font-size: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #2072ed;
    color: #ffffff;
    border-radius: 50%;
    white-space: nowrap;
    cursor: pointer;
    box-shadow: -1px 0px 4px -1px #f7f8fa;
  }
</style>

相关文章

基于ElementUi再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档


网站公告

今日签到

点亮在社区的每一天
去签到