vue 悬浮窗(带自动吸附功能)

之前写过悬浮窗的效果,这次做了个总结,网页端和移动端都可以用兼容,封装的组件代码,可以引到页面直接使用

做了简单的注释 大家自行了解

<template>
  <div
    ref="floatDrag"
    class="float-position"
    :style="{ left: left + 'px', top: top + 'px', zIndex: zIndex }"
    @touchmove.prevent
    @mousemove.prevent
    @mousedown="mouseDown"
    @mouseup="mouseUp"
  >
    <div id="side-windows">
      <div class="shrink">
      <div class="problem-feedback" @click.stop="showDialog()">
        <img :src="feedback" alt="" />
        <p>问题<br />反馈</p>
      </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "DragBall",
  props: {
    distanceRight: { // 初始化定位
      type: Number,
      default: 0
    },
    distanceBottom: { // 初始化定位
      type: Number,
      default: 100
    },
    isScrollHidden: { //滚动是否 隐藏
      type: Boolean,
      default: false
    },
    isCanDraggable: { //是否允许拖拽
      type: Boolean,
      default: true
    },
    zIndex: { // 初始化层级
      type: Number,
      default: 50
    },
    value: {
      type: String,
      default: "悬浮!"
    }
  },

  //data 域
  data() {
    return {
      clientWidth: null,
      clientHeight: null,
      left: 0,
      top: 0,
      timer: null,
      currentTop: 0,
      mousedownX: 0,
      mousedownY: 0,
      feedback: require('') // 问题
    };
  },
  created() {
    this.clientWidth = document.documentElement.clientWidth;
    this.clientHeight = document.documentElement.clientHeight;
  },
  mounted() {
    this.isCanDraggable &&
      this.$nextTick(() => {
        this.floatDrag = this.$refs.floatDrag;
        // 获取元素位置属性
        this.floatDragDom = this.floatDrag.getBoundingClientRect();
        // 设置初始位置
        this.left =
          this.clientWidth - this.floatDragDom.width - this.distanceRight;
        this.top =
          this.clientHeight - this.floatDragDom.height - this.distanceBottom;
        this.initDraggable();
      });
    this.isScrollHidden && window.addEventListener("scroll", this.handleScroll);
    window.addEventListener("resize", this.handleResize);
  },
  methods: {
      showDialog() {
        let url
        window.open(url, '_blank')
      },
    /**
     * 设置滚动时隐藏悬浮按钮,停止时显示
     */
    handleScroll() {
      this.timer && clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        this.handleScrollEnd();
      }, 200);
      this.currentTop =
        document.documentElement.scrollTop || document.body.scrollTop;
      if (this.left > this.clientWidth / 2) {
        // 判断元素位置再左侧还是右侧
        this.left = this.clientWidth + this.floatDragDom.width;
      } else {
        this.left = -this.floatDragDom.width;
      }
    },
    /**
     * 滚动结束
     */
    handleScrollEnd() {
      let scrollTop =
        document.documentElement.scrollTop || document.body.scrollTop;
      if (scrollTop === this.currentTop) {
        console.log(this.left);
        if (this.left > this.clientWidth / 2) {
          // 判断元素位置
          this.left = this.clientWidth - this.floatDragDom.width;
        } else {
          this.left = 0;
        }
        clearTimeout(this.timer);
      }
    },
    /**
     * 窗口监听
     */
    handleResize() {
      this.clientWidth = document.documentElement.clientWidth;
      this.clientHeight = document.documentElement.clientHeight;
      this.checkDraggablePosition();
    },
    /**
     * 初始化
     */
    initDraggable() {
      this.floatDrag.addEventListener("touchstart", this.toucheStart);
      this.floatDrag.addEventListener("touchmove", e => this.touchMove(e));
      this.floatDrag.addEventListener("touchend", this.touchEnd);
    },
    mouseDown(e) {
      const event = e || window.event;
      this.mousedownX = event.screenX;
      this.mousedownY = event.screenY;
      const that = this;
      let floatDragWidth = this.floatDragDom.width / 2;
      let floatDragHeight = this.floatDragDom.height / 2;
      if (event.preventDefault) {
        event.preventDefault();
      }
      this.canClick = false;
      this.floatDrag.style.transition = "none";
      setTimeout(() => {
        document.onmousemove = function(e) {
            var event = e || window.event;
            that.left = event.clientX - floatDragWidth;
            that.top = event.clientY - floatDragHeight;
            if (that.left < 0) that.left = 0;
            if (that.top < 0) that.top = 0;
            if (that.left >= that.clientWidth - floatDragWidth * 2) {
                that.left = that.clientWidth - floatDragWidth * 2;
            }
            if (that.top >= that.clientHeight - floatDragHeight * 2) {
                that.top = that.clientHeight - floatDragHeight * 2;
            }

            // 解决鼠标移出窗口 松开鼠标后 回到窗口内 悬浮继续跟随问题
            if(event.clientX<=0 || event.clientY<=0 ||  event.clientY>= that.clientHeight || event.clientX>= that.clientWidth){
                that.mouseUp(event)
            }
        };
      }, 20);

    },
    mouseUp(e) {
      const event = e || window.event;
      //判断只是单纯的点击,没有拖拽
      if (
        this.mousedownY == event.screenY &&
        this.mousedownX == event.screenX
      ) {
        this.$emit("handlepaly");
      }
      document.onmousemove = null;
      this.checkDraggablePosition();
      this.floatDrag.style.transition = "all 0.3s";
    },
    toucheStart() {
      this.canClick = false;
      this.floatDrag.style.transition = "none";
    },
    touchMove(e) {
      this.canClick = true;
      if (e.targetTouches.length === 1) {
        let touch = event.targetTouches[0];
        this.left = touch.clientX - this.floatDragDom.width / 2;
        this.top = touch.clientY - this.floatDragDom.height / 2;
      }
    },
    touchEnd() {
      if (!this.canClick) return; // 解决点击事件和touch事件冲突的问题
      this.floatDrag.style.transition = "all 0.3s";
      this.checkDraggablePosition();
    },
    /**
     * 判断元素显示位置
     * 在窗口改变和move end时调用
     */
    checkDraggablePosition() {
      let details = document.querySelector('.details')
      if (this.left + this.floatDragDom.width / 2 >= this.clientWidth / 2) {
        // 判断位置是往左往右滑动
        this.left = this.clientWidth - this.floatDragDom.width;
        details.style.right = '56px'
      } else {
        this.left = 0;
        details.style.right = '-233px'
      }
      if (this.top < 0) {
        // 判断是否超出屏幕上沿
        this.top = 0;
      }
      if (this.top + this.floatDragDom.height >= this.clientHeight) {
        // 判断是否超出屏幕下沿
        this.top = this.clientHeight - this.floatDragDom.height;
      }
    }
  },
  beforeDestroy() {
    window.removeEventListener("scroll", this.handleScroll);
    window.removeEventListener("resize", this.handleResize);
  }
};
</script>

<style lang="less" scoped>
.float-position{
  font-size: 12px;
  position: fixed;
  z-index: 500!important;
  right: 0;
  top: 50%;
  width: 48px;
  height: 168px;
  display: flex;
  align-items: center;
  justify-content: center;
  user-select: none;
}
</style>
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,可以使用vue的指令和事件来实现悬浮触发查询功能。具体可以按照以下步骤进行: 1. 在需要悬浮触发查询功能的元素上添加一个指令,例如v-hover,同时绑定一个事件处理函数,例如@mouseenter。 ``` <template> <div v-hover @mouseenter="handleHover">悬浮触发查询</div> </template> ``` 2. 在事件处理函数中调用查询接口,可以使用axios或者其他ajax库发送请求,获取查询结果并展示在页面上。 ``` <script> import axios from 'axios' export default { methods: { async handleHover() { try { const res = await axios.get('/api/query') // 处理查询结果,展示在页面上 } catch (error) { console.error(error) } } } } </script> ``` 3. 根据需求可以添加一些样式和交互效果,例如使用v-show控制查询结果的显示和隐藏。 ``` <template> <div v-hover @mouseenter="handleHover" @mouseleave="hideResult"> 悬浮触发查询 <div v-show="showResult">查询结果</div> </div> </template> <script> import axios from 'axios' export default { data() { return { showResult: false, resultData: null } }, methods: { async handleHover() { try { const res = await axios.get('/api/query') this.resultData = res.data this.showResult = true } catch (error) { console.error(error) } }, hideResult() { this.showResult = false } } } </script> <style> div { position: relative; } div > div { position: absolute; top: 100%; left: 0; background-color: white; border: 1px solid #ccc; padding: 10px; } </style> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值