vue2 实现可拖拽悬浮球

  • 实现效果

  • 相关代码

点击查看代码

<template>
  <div class="float-box">
    <div
      class="button-box"
      @mousedown="mousedown"
      @mousemove="mousemove"
      @touchmove="mousemove"
      @mouseup="mouseup"
      @touchstart="mousedown"
      @touchend="mouseup"
      @click="onClick"
      ref="flbutton"
    >
      <span class="title">{{ title }}</span>
    </div>
    <div
      :style="{ left: left + 'px', top: top + 'px' }"
      v-if="menuFlag"
      :class="menuPosition === 'right' ? 'menu-item-right':'menu-item-left'"
    >
      <div :class="tabBackImg(index)" class="box-title-box-item" v-for="(item,index) in circleList"
           @click="circleClick(item,index)">
        <span :class="'text'+index">{{ item }}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "FloatButton",
  props: {
    circleList: {
      type: Array,
      default: () => []
    },
  },
  data() {
    return {
      title: '',
      left: 0,
      top: 40,
      menuFlag: false,//菜单显隐
      mouseDownflag: false, // 鼠标点击状态
      position: {
        x: 0,
        y: 0,
      },
      nx: "",
      ny: "",
      dx: "",
      dy: "",
      xPum: "",
      yPum: "",
      movb: 1,//区分拖拽还是点击
      menuPosition: 'right',
    };
  },
  mounted() {
    this.left = this.$refs.flbutton.offsetLeft + 20;
    this.top = this.$refs.flbutton.offsetTop;
    this.title = this.circleList[0];
  },
  computed: {
    tabBackImg() {
      return (index) => {
        if (index === 0) {
          return 'tab-item-1'
        } else if (index === 1) {
          return 'tab-item-2'
        } else if (index === 2) {
          return 'tab-item-3'
        } else if (index === 3) {
          return 'tab-item-4'
        }
      }
    }
  },
  methods: {
    circleClick(item, index) {
      this.title = item
      this.$emit('circleClick', item, index)
    },
    //鼠标摁下
    mousedown() {
      this.mouseDownflag = true;
      /*此处判断pc 或移动端得到event 事件*/
      var touch;
      if (event.touches) {
        touch = event.touches[0];
      } else {
        touch = event;
      }
      //鼠标点击面向页面的x坐标y坐标
      this.position.x = touch.clientX;
      this.position.y = touch.clientY;
      //鼠标x坐标-拖拽按钮x坐标得到鼠标距离拖拽按钮的间距
      this.dx = this.$refs.flbutton.offsetLeft;
      //鼠标y坐标-拖拽按钮y坐标得到鼠标距离拖拽按钮的间距
      this.dy = this.$refs.flbutton.offsetTop;
    },
    //鼠标拖拽移动
    mousemove() {
      if (this.mouseDownflag) {
        this.movb = 2;
        this.menuFlag = false;
        /*此处判断得到event事件*/
        var touch;
        if (event.touches) {
          touch = event.touches[0];
        } else {
          touch = event;
        }
        //鼠标坐标-鼠标与拖拽按钮的间距坐标得到拖拽按钮的左上角x轴y轴坐标
        this.nx = touch.clientX - this.position.x;
        this.ny = touch.clientY - this.position.y;

        this.xPum = this.dx + this.nx;
        this.yPum = this.dy + this.ny;
        let width = window.innerWidth - this.$refs.flbutton.offsetWidth; //屏幕宽度减去自身控件宽度
        let height = window.innerHeight - this.$refs.flbutton.offsetHeight; //屏幕高度减去自身控件高度
        /* 此处判断
          拖拽按钮 如果超出屏幕宽高或者小于
          设置屏幕最大 x=全局容器x y=全局容器y
          否则 设置 为 x=0 y=0
        */
        this.xPum < 0 && (this.xPum = 0);
        this.yPum < 0 && (this.yPum = 0);
        this.xPum > width && (this.xPum = width);
        this.yPum > height && (this.yPum = height);
        if (this.xPum > 900) {
          this.menuPosition = 'left'
        } else {
          this.menuPosition = 'right'
        }
        // 计算后坐标  设置 按钮位置
        this.$refs.flbutton.style.left = this.xPum + "px";
        this.$refs.flbutton.style.top = this.yPum + "px";
        this.left = this.xPum + 20;
        this.top = this.yPum;

        //阻止页面的滑动默认事件
        document.addEventListener("touchmove", function () {
          event.preventDefault();
        }, false);
      }
    },
    //鼠标抬起
    mouseup() {
      this.mouseDownflag = false;
    },
    onClick() {
      if (this.movb === 2) {
        this.movb = 1;
      } else {
        this.menuFlag = !this.menuFlag;
      }
    },
  },
};
</script>
<style scoped lang="scss">
.float-box {
  position: relative;

  .button-box {
    width: 110px;
    height: 110px;
    position: fixed;
    top: 100px;
    left: 500px;
    touch-action: none;
    text-align: center;
    color: white;
    background-image: url("~@/assets/images/screen/float-button/button.png");
    background-repeat: no-repeat;
    background-size: 100% 100%;
    line-height: 100px;
    font-size: 14px;
    cursor: pointer;
    z-index: 99;

    .title {
      background: linear-gradient(rgba(255, 255, 255, 1) 25%, rgba(69, 177, 254, 1) 100%);;
      -webkit-background-clip: text;
      color: transparent;
      font-weight: bolder;
    }
  }

  .menu-item-left {
    position: absolute;
    z-index: 99;

    .box-title-box-item {
      color: #FFFFFF;
      cursor: pointer;
      font-size: 13px;
      cursor: pointer;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .tab-item-1 {
      height: 53px;
      width: 43px;
      position: absolute;
      top: -8px;
      left: -42px;
      background: url("~@/assets/images/screen/float-button/button-l1.png") no-repeat;
      background-size: 100% 100%;
      line-height: 43px;
      padding-top: 10px;
    }

    .tab-item-2 {
      height: 53px;
      width: 44px;
      background: url("~@/assets/images/screen/float-button/button-l2.png") no-repeat;
      background-size: 100% 100%;
      position: absolute;
      top: 46px;
      left: -42px;
      line-height: 43px;
    }

    .tab-item-3 {
      height: 43px;
      width: 53px;
      background: url("~@/assets/images/screen/float-button/button-r3.png") no-repeat;
      background-size: 100% 100%;
      position: absolute;
      top: 80px;
      left: 37px;
      line-height: 43px;
    }

    .tab-item-4 {
      height: 43px;
      width: 53px;
      background: url("~@/assets/images/screen/float-button/button-r4.png") no-repeat;
      background-size: 100% 100%;
      position: absolute;
      top: 80px;
      left: -17px;
      line-height: 40px;
    }

    .tab-item-1:hover, .tab-item-2:hover, .tab-item-3:hover, .tab-item-4:hover {
      transform: scale(1.2);
    }

    .text0, .text1, .text2, .text3 {
      display: inline-block;
    }

    .text0 {
      writing-mode: vertical-rl;
      letter-spacing: 1px;
      transform: rotate(17deg);
    }

    .text1 {
      writing-mode: vertical-rl;
      letter-spacing: 1px;
      transform: rotate(335deg);
    }

    .text2 {
      transform: rotate(340deg);
    }

    .text3 {
      transform: rotate(20deg);
    }
  }

  .menu-item-right {
    position: absolute;
    z-index: 99;

    .box-title-box-item {
      color: #FFFFFF;
      cursor: pointer;
      font-size: 13px;
      cursor: pointer;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .tab-item-1 {
      height: 53px;
      width: 43px;
      position: absolute;
      top: -10px;
      left: 67px;
      background: url("~@/assets/images/screen/float-button/button-r1.png") no-repeat;
      background-size: 100% 100%;
      line-height: 43px;
      padding-top: 10px;
    }

    .tab-item-2 {
      height: 53px;
      width: 43px;
      background: url("~@/assets/images/screen/float-button/button-r2.png") no-repeat;
      background-size: 100% 100%;
      position: absolute;
      top: 45px;
      left: 69px;
      line-height: 43px;
    }

    .tab-item-3 {
      height: 43px;
      width: 53px;
      background: url("~@/assets/images/screen/float-button/button-r3.png") no-repeat;
      background-size: 100% 100%;
      position: absolute;
      top: 80px;
      left: 35px;
      line-height: 43px;
    }

    .tab-item-4 {
      height: 43px;
      width: 53px;
      background: url("~@/assets/images/screen/float-button/button-r4.png") no-repeat;
      background-size: 100% 100%;
      position: absolute;
      top: 80px;
      left: -20px;
      line-height: 40px;
    }

    .tab-item-1:hover, .tab-item-2:hover, .tab-item-3:hover, .tab-item-4:hover {
      transform: scale(1.2);
    }

    .text0, .text1, .text2, .text3 {
      display: inline-block;
    }

    .text0 {
      writing-mode: vertical-rl;
      letter-spacing: 1px;
      transform: rotate(345deg);
    }

    .text1 {
      writing-mode: vertical-rl;
      letter-spacing: 1px;
      transform: rotate(21deg);
      margin-bottom: 5px;
    }

    .text2 {
      transform: rotate(337deg);
      margin-right: 10px;
    }

    .text3 {
      transform: rotate(20deg);
      margin-left: 5px;
    }
  }
}
</style>
相关推荐
落魄实习生6 小时前
AI应用-本地模型实现AI生成PPT(简易版)
python·ai·vue·ppt
bpmf_fff8 小时前
二九(vue2-05)、父子通信v-model、sync、ref、¥nextTick、自定义指令、具名插槽、作用域插槽、综合案例 - 商品列表
vue
java_heartLake14 小时前
Vue3之状态管理Vuex
vue·vuex·前端状态管理
小马超会养兔子14 小时前
如何写一个数字老虎机滚轮
开发语言·前端·javascript·vue
小阳生煎17 小时前
多个Echart遍历生成 / 词图云
vue
小马超会养兔子2 天前
如何写一个转盘
开发语言·前端·vue
bpmf_fff2 天前
二八(vue2-04)、scoped、data函数、父子通信、props校验、非父子通信(EventBus、provide&inject)、v-model进阶
vue
好开心332 天前
04、Vue与Ajax
前端·ajax·前端框架·vue·js
工业互联网专业3 天前
Python毕业设计选题:基于Python的社区爱心养老管理系统设计与实现_django
python·django·vue·毕业设计·源码·课程设计
平行线也会相交3 天前
云图库平台(一)后端项目初始化
spring boot·vue·云图库平台