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>
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue3是最新版本的Vue.js框架,它具有更强大的功能和性能优化,可以实现可拖动弹窗的封装。 首先,我们可以利用Vue3的自定义指令来实现拖动效果。首先在弹窗组件中添加一个容器元素,用来表示弹窗的位置和大小。然后,在该容器元素上添加一个自定义指令,比如"v-draggable"。在该自定义指令中,我们可以通过监听鼠标事件来实现拖动功能。当鼠标按下时,记录当前鼠标的位置坐标,并监听鼠标移动事件,不断地更新弹窗容器的位置坐标。最后,在鼠标松开的时候,停止拖动。 接下来,我们可以为弹窗组件添加一些控制按钮,比如关闭按钮和最小化按钮。这些按钮可以通过Vue3的事件绑定来实现对应的功能。例如,点击关闭按钮后,可以通过触发一个"close"事件来关闭弹窗。 此外,我们还可以利用Vue3的响应式数据和计算属性来实现弹窗的位置和大小自动调整。通过监听窗口大小变化的事件,并实时更新弹窗容器的位置和大小,使之适应不同的显示设备和分辨率。 最后,为了防止拖动时超出边界的情况,我们可以在自定义指令中添加一些边界限制的判断逻辑,以确保弹窗不会超出容器范围。 综上所述,通过Vue3的自定义指令、事件绑定、响应式数据和计算属性,我们可以很方便地封装一个可拖动的弹窗组件,并且可以根据需要添加其他功能和样式,以满足不同的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

四岁爱上了她

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值