页面悬浮按钮可拖动实现(vue2和vue3写法)

注:引入https://blog.csdn.net/qq_34684704/article/details/119952404

 

 

vue2版本

<template>
  <div
    ref="floatDrag"
    class="float-position"
    :style="{ left: left + 'px', top: top + 'px', right: right + 'px !important', zIndex: zIndex }"
    @touchmove.prevent
    @mousemove.prevent
    @mousedown="mouseDown"
    @mouseup="mouseUp"
  >
    <div class="content">
      <!-- <div>
        <span class="close">
          <Icon icon="icon-guanbi" :size="18" />
        </span>
        <Icon icon="icon-xuanzhong" :size="36" style="color: #fff" />
      </div> -->
      <img src="../../../assets/images/uploadLoading.png" alt="" />
    </div>
  </div>
</template>

<script>
  import { Icon } from '/@/components/Icon';
  export default {
    components: {
      Icon,
    },
    name: 'DragBall',
    props: {
      distanceRight: {
        type: Number,
        default: 36,
      },
      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: null,
        top: null,
        right: null,
        timer: null,
        currentTop: 0,
        mousedownX: 0,
        mousedownY: 0,
      };
    },
    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.right = 0;
          this.top = this.clientHeight - this.floatDragDom.height - this.distanceBottom;
          this.initDraggable();
        });
      // this.isScrollHidden && window.addEventListener('scroll', this.handleScroll);
      window.addEventListener('resize', this.handleResize);
    },
    beforeUnmount() {
      window.removeEventListener('scroll', this.handleScroll);
      window.removeEventListener('resize', this.handleResize);
    },
    methods: {
      /**
       * 窗口resize监听
       */
      handleResize() {
        // this.clientWidth = document.documentElement.clientWidth;
        // this.clientHeight = document.documentElement.clientHeight;
        // console.log(window.innerWidth);
        // console.log(document.documentElement.clientWidth);

        this.checkDraggablePosition();
      },
      /**
       * 初始化draggable
       */
      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';
        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 (
            event.clientY < 0 ||
            event.clientY > Number(this.clientHeight) ||
            event.clientX > Number(this.clientWidth) ||
            event.clientX < 0
          ) {
            this.right = 0;
            this.top = this.clientHeight - this.floatDragDom.height - this.distanceBottom;
            document.onmousemove = null;
            this.floatDrag.style.transition = 'all 0.3s';
            return;
          }
          if (that.left >= document.documentElement.clientWidth - floatDragWidth * 2) {
            that.left = document.documentElement.clientWidth - floatDragWidth * 2;
          }
          if (that.top >= that.clientHeight - floatDragHeight * 2) {
            that.top = that.clientHeight - floatDragHeight * 2;
          }
        };
      },
      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() {
        this.clientWidth = document.documentElement.clientWidth;
        this.clientHeight = document.documentElement.clientHeight;
        if (this.left + this.floatDragDom.width / 2 >= this.clientWidth / 2) {
          // 判断位置是往左往右滑动
          this.left = this.clientWidth - this.floatDragDom.width;
        } else {
          this.left = 0;
        }
        if (this.top < 0) {
          // 判断是否超出屏幕上沿
          this.top = 0;
        }
        if (this.top + this.floatDragDom.height >= this.clientHeight) {
          // 判断是否超出屏幕下沿
          this.top = this.clientHeight - this.floatDragDom.height;
        }
      },
    },
  };
</script>
<style>
  html,
  body {
    overflow: hidden;
  }
</style>
<style scoped lang="less">
  .float-position {
    position: fixed;
    z-index: 10003 !important;
    right: 0;
    top: 70%;
    width: 70px;
    height: 70px;
    background: pink;
    // background: #716af2;
    // box-shadow: 0px 0px 10px 2px #a299ff;
    // border-radius: 50%;
    // overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    user-select: none;
    border-right: 3px dashed #c00;
    .content {
      width: 50px;
      height: 50px;
      background: #716af2;
      box-shadow: 0px 0px 10px 2px #a299ff;
      border-radius: 50%;
      position: relative;
      padding: 0.8em;
      display: flex;
      align-content: center;
      justify-content: center;
    }
    .close {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
      background: rgba(0, 0, 0, 0.6);
      position: absolute;
      right: -10px;
      top: -12px;
      cursor: pointer;
    }
  }
  .cart {
    border-radius: 50%;
    width: 5em;
    height: 5em;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .header-notice {
    display: inline-block;
    transition: all 0.3s;
    span {
      vertical-align: initial;
    }
    .notice-badge {
      color: inherit;
      .header-notice-icon {
        font-size: 16px;
        padding: 4px;
      }
    }
  }
  .drag-ball .drag-content {
    overflow-wrap: break-word;
    font-size: 14px;
    color: #fff;
    letter-spacing: 2px;
  }
</style>

 vue3写法

<template>
  <div
    ref="floatDrag"
    id="floatDrag"
    class="float-position"
    :style="{ top: data.top + 'px', right: data.right + 'px !important' }"
    @touchmove.prevent
    @mousemove.prevent
    @mousedown="mouseDown"
    @mouseup="mouseUp"
  >
    <div class="content">
      <!-- <div>
        <span class="close">
          <Icon icon="icon-guanbi" :size="18" />
        </span>
        <Icon icon="icon-xuanzhong" :size="36" style="color: #fff" />
      </div> -->
      <img src="../../../assets/images/uploadLoading.png" alt="" />
    </div>
  </div>
</template>

<script lang="ts">
  import { defineComponent, onMounted, reactive, nextTick, ref, onBeforeMount } from 'vue';
  import { Icon } from '/@/components/Icon';
  interface dataOptionItem {
    clientWidth: null | number;
    clientHeight: null | number;
    left: number;
    top: number;
    right: number;
    timer: null | number;
    currentTop: 0 | number;
    mousedownX: 0 | number;
    mousedownY: 0 | number;
  }
  export default defineComponent({
    name: 'DragBall',
    components: {
      Icon,
    },
    props: {
      distanceRight: {
        type: Number,
        default: 36,
      },
      distanceBottom: {
        type: Number,
        default: 100,
      },
      isScrollHidden: {
        type: Boolean,
        default: false,
      },
      isCanDraggable: {
        type: Boolean,
        default: true,
      },
    },
    emits: ['handlepaly'],
    setup(props, { emit }) {
      const data = reactive<dataOptionItem>({
        clientWidth: null,
        clientHeight: null,
        left: 0,
        top: 0,
        right: 0,
        timer: null,
        currentTop: 0,
        mousedownX: 0,
        mousedownY: 0,
      });
      // const floatDrag = ref();
      const floatDragDom = ref();
      data.clientWidth = document.documentElement.clientWidth;
      data.clientHeight = document.documentElement.clientHeight;
      onMounted(() => {
        props.isCanDraggable &&
          nextTick(() => {
            // 获取元素位置属性
            floatDragDom.value = document.getElementById('floatDrag');
            // 设置初始位置
            data.right = props.distanceRight;
            data.top =
              Number(data.clientHeight) - floatDragDom.value?.offsetHeight - props.distanceBottom;
            initDraggable();
          });
        // this.isScrollHidden && window.addEventListener('scroll', this.handleScroll);
        window.addEventListener('resize', handleResize);
      });
      onBeforeMount(() => {
        // window.removeEventListener('scroll', this.handleScroll);
        window.removeEventListener('resize', handleResize);
      });
      /**
       * 初始化draggable
       */
      const initDraggable = () => {
        floatDragDom.value.addEventListener('touchstart', toucheStart);
        floatDragDom.value.addEventListener('touchmove', (e) => touchMove(e));
        floatDragDom.value.addEventListener('touchend', touchEnd);
      };
      const handleResize = () => {
        checkDraggablePosition();
      };
      /**
       * 判断元素显示位置
       * 在窗口改变和move end时调用
       */
      const checkDraggablePosition = () => {
        data.clientWidth = document.documentElement.clientWidth;
        data.clientHeight = document.documentElement.clientHeight;
        if (data.right + floatDragDom.value.offsetWidth / 2 >= data.clientWidth / 2) {
          // 判断位置是往左往右滑动
          data.right = data.clientWidth - floatDragDom.value.offsetWidth;
        } else {
          data.right = 0;
        }
        if (data.top < 0) {
          // 判断是否超出屏幕上沿
          data.top = 0;
        }
        if (data.top + floatDragDom.value.offsetHeight >= data.clientHeight) {
          // 判断是否超出屏幕下沿
          data.top = data.clientHeight - floatDragDom.value.offsetHeight;
        }
      };
      const canClick = ref(false);
      const mouseDown = (e) => {
        const event = e || window.event;
        data.mousedownX = event.screenX;
        data.mousedownY = event.screenY;
        let floatDragWidth = floatDragDom.value.offsetWidth / 2;
        let floatDragHeight = floatDragDom.value.offsetHeight / 2;
        if (event.preventDefault) {
          event.preventDefault();
        }
        canClick.value = false;
        floatDragDom.value.style.transition = 'none';
        document.onmousemove = function (e) {
          var event = e || window.event;
          console.log(event.clientY);
          // 这里的判断是为了保证按钮只能在浏览器内拖动,不会超出
          data.right = Number(data.clientWidth) - event.clientX - floatDragWidth;
          data.top = event.clientY - floatDragHeight;
          if (data.right < 0) data.right = 0;
          if (data.top < 0) data.top = 0;
          // 鼠标移出可视区域后给按钮还原
          if (
            event.clientY < 0 ||
            event.clientY > Number(data.clientHeight) ||
            event.clientX > Number(data.clientWidth) ||
            event.clientX < 0
          ) {
            data.right = 0;
            data.top =
              Number(data.clientHeight) - floatDragDom.value?.offsetHeight - props.distanceBottom;
            document.onmousemove = null;
            floatDragDom.value.style.transition = 'all 0.3s';
            return;
          }
          if (data.right >= document.documentElement.clientWidth - floatDragWidth * 2) {
            data.right = document.documentElement.clientWidth - floatDragWidth * 2;
          }
          if (data.top >= Number(data.clientHeight) - floatDragHeight * 2) {
            data.top = Number(data.clientHeight) - floatDragHeight * 2;
          }
        };
      };
      const mouseUp = (e) => {
        const event = e || window.event;
        //判断只是单纯的点击,没有拖拽
        if (data.mousedownY == event.screenY && data.mousedownX == event.screenX) {
          emit('handlepaly');
        }
        document.onmousemove = null;
        checkDraggablePosition();
        floatDragDom.value.style.transition = 'all 0.3s';
      };
      const toucheStart = () => {
        canClick.value = false;
        floatDragDom.value.style.transition = 'none';
      };
      const touchMove = (e) => {
        canClick.value = true;
        if (e.targetTouches.length === 1) {
          // 单指拖动
          let touch = e.targetTouches[0];
          data.right =
            Number(data.clientWidth) - touch.clientX - floatDragDom.value.offsetWidth / 2;
          data.top = touch.clientY - floatDragDom.value.offsetHeight / 2;
        }
      };
      const touchEnd = () => {
        if (!canClick.value) return; // 解决点击事件和touch事件冲突的问题
        floatDragDom.value.style.transition = 'all 0.3s';
        checkDraggablePosition();
      };
      return {
        data,
        handleResize,
        checkDraggablePosition,
        mouseUp,
        mouseDown,
      };
    },
  });
</script>

<style>
  html,
  body {
    overflow: hidden;
  }
</style>
<style scoped lang="less">
  .float-position {
    position: fixed;
    z-index: 99999999 !important;
    right: 0;
    top: 70%;
    width: 70px;
    height: 70px;
    display: flex;
    align-items: center;
    justify-content: center;
    user-select: none;
    .content {
      width: 50px;
      height: 50px;
      background: #716af2;
      box-shadow: 0px 0px 10px 2px #a299ff;
      border-radius: 50%;
      position: relative;
      padding: 0.8em;
      display: flex;
      align-content: center;
      justify-content: center;
    }
    .close {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
      background: rgba(0, 0, 0, 0.6);
      position: absolute;
      right: -10px;
      top: -12px;
      cursor: pointer;
    }
  }
  .cart {
    border-radius: 50%;
    width: 5em;
    height: 5em;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .header-notice {
    display: inline-block;
    transition: all 0.3s;
    span {
      vertical-align: initial;
    }
    .notice-badge {
      color: inherit;
      .header-notice-icon {
        font-size: 16px;
        padding: 4px;
      }
    }
  }
  .drag-ball .drag-content {
    overflow-wrap: break-word;
    font-size: 14px;
    color: #fff;
    letter-spacing: 2px;
  }
</style>

 

posted @ 2022-06-24 14:56  杰_森  阅读(4042)  评论(1编辑  收藏  举报