Bootstrap

vue3封装拖拽自定义组件

在这里插入图片描述

这个拖拽指令需要用到被拖拽的元素上,然后父元素就不需要写定位属性了,因为在指令里面会给父元素添加定位属性的

先写好指令文件

// 第一种
const vDrag = {

  mounted(el, binding) {
    // 设置目标元素基础属性
    el.style.cursor = 'move';
    el.style.position = 'fixed';
    // 获取容器宽高
    let parentNode = el.parentNode || null;
    let containerWidth = window.innerWidth - getScrollWidth();
    let containerHeight = window.innerHeight;
    // 存在父级容器
    if (parentNode) {
      parentNode.style.position = "relative"
      containerWidth = parentNode.offsetWidth;
      containerHeight = parentNode.offsetHeight;
      // 防止选中移动块上的文字等
      parentNode.onmouseover = () => {
        document.onselectstart = () => {
          return false;
        };
      };
      parentNode.onmouseout = () => {
        document.onselectstart = () => {
          return true;
        };
      };
      el.style.position = 'absolute';
    }
    // 鼠标在目标元素上按下        
    el.addEventListener('mousedown', (e) => {
      let { width, height } = el.getBoundingClientRect();
      // 当前目标元素的left与top
      const left = el.offsetLeft;
      const top = el.offsetTop;
      // 保存按下的鼠标的X与Y
      const mouseX = e.clientX;
      const mouseY = e.clientY;
      // 计算边界值
      const leftLimit = left;
      const rightLimit = containerWidth - left - width;
      const topLimit = top;
      const bottomLimit = containerHeight - top - height;

      // 监听鼠标移动
      document.onmousemove = (e) => {
        // 鼠标移动的距离
        let disX = e.clientX - mouseX;
        let disY = e.clientY - mouseY;
        // 左右边界
        if (disX < 0 && disX <= -leftLimit) {
          el.style.left = (left - leftLimit) + 'px';
        } else if (disX > 0 && disX >= rightLimit) {
          el.style.left = (left + rightLimit) + 'px';
        } else {
          el.style.left = (left + disX) + 'px';
        }
        // 上下边界
        if (disY < 0 && disY <= -topLimit) {
          el.style.top = (top - topLimit) + 'px';
        } else if (disY > 0 && disY >= bottomLimit) {
          el.style.top = (top + bottomLimit) + 'px';
        } else {
          el.style.top = (top + disY) + 'px';
        }
        return false;
      }

      // 监听鼠标抬起
      document.onmouseup = () => {
        document.onmousemove = null;
        document.onmouseup = null;
      }



    });

    // 获取元素的相关CSS
    function getStyle(el, attr) {
      return el.currentStyle ? el.currentStyle[attr] : window.getComputedStyle(el, false)[attr];
    }

    // 返回滚动条的宽度, 没有则返回0
    function getScrollWidth() {
      let noScroll, scroll, oDiv = document.createElement("DIV");
      oDiv.style.cssText = "position:absolute; top:-1000px; width:100px; height:100px; overflow:hidden;";
      noScroll = document.body.appendChild(oDiv).clientWidth;
      oDiv.style.overflowY = "scroll";
      scroll = oDiv.clientWidth;
      document.body.removeChild(oDiv);
      let isExsit = document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight);
      return isExsit ? noScroll - scroll : 0
    }
  }
}

// 第二种
const vDragTwo = {
  mounted(el, binding, vnode, oldVnode) {
    let odiv = el; //获取当前元素
    // 获取可移动元素的父节点
    let parentNode = el.parentNode;
    // 设置父节点定位
    parentNode.style.position = "relative";
    el.style.position = "absolute";

    // 设置鼠标hover效果:移动上前去显示可移动的提示效果,并且禁用页面可选择,离开恢复正常
    el.onmouseover = () => {
      el.style.cursor = "move";
    };
    el.onmouseout = () => {
      el.style.cursor = "none";
    };
    // 防止选中移动块上的文字等
    parentNode.onmouseover = () => {
      document.onselectstart = () => {
        return false;
      };
    };
    parentNode.onmouseout = () => {
      document.onselectstart = () => {
        return true;
      };
    };

    el.onmousedown = (event) => {
      //event的兼容,同时得到clientX,的值
      var event = event || window.event;
      let x = event.clientX - el.offsetLeft;
      let y = event.clientY - el.offsetTop; //得到小段的偏移
      // 将移动事件绑定到 document 上,防止拖动过快脱离开
      document.onmousemove = (event) => {
        let xx = event.clientX - x; //当移动的时候,用它的鼠标值减去偏移量
        let yy = event.clientY - y;
        if (xx <= 0) {
          //判定边界值 0,就在最边上了,
          xx = 0;
        }
        if (xx >= parentNode.offsetWidth - el.offsetWidth) {
          //大于整个盒子的宽度-小盒子的宽度
          xx = parentNode.offsetWidth - el.offsetWidth;
        }
        if (yy <= 0) {
          yy = 0;
        }
        if (yy >= parentNode.offsetHeight - el.offsetHeight) {
          yy = parentNode.offsetHeight - el.offsetHeight;
        }

        el.style.left = xx + "px";
        el.style.top = yy + "px";
      };
      el.onmouseup = () => {
        // 取消事件
        document.onmousemove = null;
        el.onmouseup = null;
      };
    }
  }
}
export default {
  install: (app) => {
    app.directive('drag', vDrag)
    app.directive('dragtwo', vDragTwo)
  }
}

main.js引入这个指令文件

import { createApp } from 'vue'
import App from './App.vue'
import DRAG from './utile/ZhiLing/Drag'  //这个就是指令文件

const app = createApp(App)
app.use(DRAG)
app.mount('#app')

到此已经完成了全局的注册指令了,接下来是使用

<template>
  <div class="homes">
    <div class="box_div" id="box_div">

      <div class="box" v-drag>
        <div>我是全局</div>
        <div>v-drag</div>
      </div>
      <div class="box2" v-dragarr>
        <div>我是局部</div>
        <div>v-dragarr</div>
      </div>

    </div>
    666
    <div style="width: 100px;height:100px;background-color: cornflowerblue;" v-drag>
      <div>我是全局</div>
      <div>v-drag</div>
    </div>
    <div v-for="item in 10" :key="item">{{ item }}</div>
    <div style="width: 100px;height:100px;background-color: cornflowerblue;" v-dragtwo>
      <div>我是全局</div>
      <div>v-dragtwo</div>
    </div>
    <div v-for="item in 10" :key="item">{{ item }}</div>

    <div style="width: 90%;height: 300px;border:1px solid #36d">
      <div style="width: 100px;height: 80px;background-color: #fff;" v-drag>
        <div>我是全局</div>
        <div>v-drag</div>
      </div>
    </div>
    <div v-for="item in 10" :key="item">{{ item }}</div>

  </div>
</template>
<script setup>
import { ref, onMounted, nextTick, defineComponent, createVNode } from "vue";
const orignCanvas = ref([])
const pdfUrl = ref(null)
const signUrl = ref(null)


//局部自定义指令
const vDragarr = {
  mounted(el, binding, vnode, oldVnode) {
    let odiv = el; //获取当前元素

    console.log('aa', odiv);
    console.log('bb', binding);
    console.log('cc', vnode);
    console.log('dd', oldVnode);
    // 获取可移动元素的父节点
    let parentNode = el.parentNode;
    // 设置父节点定位
    // parentNode.style.position = "relative";
    el.style.position = "absolute";

    // 设置鼠标hover效果:移动上前去显示可移动的提示效果,并且禁用页面可选择,离开恢复正常
    el.onmouseover = () => {
      el.style.cursor = "move";
    };
    el.onmouseout = () => {
      el.style.cursor = "none";
    };
    // 防止选中移动块上的文字等
    parentNode.onmouseover = () => {
      document.onselectstart = () => {
        return false;
      };
    };
    parentNode.onmouseout = () => {
      document.onselectstart = () => {
        return true;
      };
    };

    el.onmousedown = (event) => {
      // console.log('parentNode', parentNode);
      // console.log('aa', odiv);

      // console.log('bb', binding);
      // console.log('cc', vnode);
      // console.log('dd', oldVnode);
      //event的兼容,同时得到clientX,的值
      var event = event || window.event;
      let x = event.clientX - el.offsetLeft;
      let y = event.clientY - el.offsetTop; //得到小段的偏移
      // 将移动事件绑定到 document 上,防止拖动过快脱离开
      document.onmousemove = (event) => {
        let xx = event.clientX - x; //当移动的时候,用它的鼠标值减去偏移量
        let yy = event.clientY - y;
        if (xx <= 0) {
          //判定边界值 0,就在最边上了,
          xx = 0;
        }
        if (xx >= parentNode.offsetWidth - el.offsetWidth) {
          //大于整个盒子的宽度-小盒子的宽度
          xx = parentNode.offsetWidth - el.offsetWidth;
        }
        if (yy <= 0) {
          yy = 0;
        }
        if (yy >= parentNode.offsetHeight - el.offsetHeight) {
          yy = parentNode.offsetHeight - el.offsetHeight;
        }

        el.style.left = xx + "px";
        el.style.top = yy + "px";
      };
      el.onmouseup = () => {
        // 取消事件
        document.onmousemove = null;
        el.onmouseup = null;
      };
    }
  },
}
</script>
<style lang="less">
.homes {
  width: 100%;
  height: 100%;
  overflow: auto;

  .box_div {
    width: 50%;
    height: 500px;
    margin-left: 100px;
    background-color: #84d496;
    position: relative;

    .box {
      width: 100px;
      height: 100px;
      background-color: coral;
    }

    .box2 {
      width: 100px;
      height: 100px;
      top: 150px;
      left: 0px;
      background-color: rgb(80, 83, 255);
    }
  }
}
</style>
;