vue自定义指令实现任意标签可拖拽、改变标签宽度、动态拖拽交换位置

拖拽原理及相关定义

在这里插入图片描述

自定义指令实现

在utils目录下建立directives.js文件

import Vue from 'vue'
// v-drag: 拖拽
Vue.directive('drag', {
  bind(el, binding, vnode, oldVnode) {
  	//el即为当前元素
    el.style.cursor = 'move'
    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = el.currentStyle || window.getComputedStyle(el, null)
    el.onmousedown = (e) => {
      //获取鼠标按下位置
      const disX = e.clientX
      const disY = e.clientY
      // 获取当前元素的定位信息
      // 获取到的值带px 正则匹配替换
      let styL, styT
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      // +的作用是将字符串转为数字
      if (sty.left.includes('%')) {
        styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100)
        styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100)
      } else {
        styL = +sty.left.replace(/\px/g, '')
        styT = +sty.top.replace(/\px/g, '')
      }
      document.onmousemove = function(e) {
        // 通过事件委托,计算移动的距离
        const l = e.clientX - disX
        const t = e.clientY - disY
        // 移动当前元素
        el.style.left = `${l + styL}px`
        el.style.top = `${t + styT}px`
      }
	  //鼠标弹起,移除相应事件
      document.onmouseup = function(e) {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
})

// v-dragWidth: 标签宽度拖大 拖小
Vue.directive('dragWidth', {
  bind(el, binding, vnode, oldVnode) {
  	el.style.cursor = 'crosshair'
  	// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
    const sty = el.currentStyle || window.getComputedStyle(el, null)
    el.onmousedown = (e) => {
      const disX = e.clientX
      // 获取到的值带px 正则匹配替换
      let styW
      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
      if (sty.left.includes('%')) {
        styW = +document.body.clientWidth * (+sty.width.replace(/\%/g, '') / 100)
      } else {
        styW = +sty.width.replace(/\px/g, '')
      }
      document.onmousemove = function(e) {
        e.preventDefault() // 移动时禁用默认事件
        // 通过事件委托,计算移动的距离
        const l = e.clientX - disX
        el.style.width = `${styW + l}px`
      }
      document.onmouseup = function(e) {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
})

指令使用
main.js中引入

import './utils/directives.js';

组件内使用

<div v-drag></div>
<div v-dragWidth></div>

动态拖拽交换位置——draggable指令

<template>
  <transition-group tag="div" class="container">
    <div class="item"
    v-for="item in items"
    :key="item.key"
    :style="{background:item.color,width:'80px',height:'80px',}"
    draggable="true"
    @dragstart="handleDragStart($event, item)"
    @dragover.prevent="handleDragOver($event, item)"
    @dragenter="handleDragEnter($event, item)"
    @dragend="handleDragEnd($event, item)" >
    </div>
  </transition-group>
</template>

<script>
export default {
  name: 'Toolbar',
  data() {
    return {
      items: [
        { key: 1, color: '#ffebcc' },
        { key: 2, color: '#ffb86c' },
        { key: 3, color: '#f01b2d' }
      ],
      dragging: null
    };
  },
  methods: {
    handleDragStart(e, item) {
      this.dragging = item;
    },
    handleDragEnd(e, item) {
      this.dragging = null;
    },
    // 首先把div变成可以放置的元素,即重写dragenter/dragover
    handleDragOver(e) {
      e.dataTransfer.dropEffect = 'move';// e.dataTransfer.dropEffect="move";//在dragenter中针对放置目标来设置!
    },
    handleDragEnter(e, item) {
      e.dataTransfer.effectAllowed = 'move';// 为需要移动的元素设置dragstart事件
      if (item === this.dragging) {
        return;
      }
      const newItems = [...this.items];
      const src = newItems.indexOf(this.dragging);
      const dst = newItems.indexOf(item);
      newItems.splice(dst, 0, ...newItems.splice(src, 1));
      this.items = newItems;
    }
  }
};

</script>

<style scoped>
.container{
  width: 80px;
  height: 300px;
  position: absolute;
  left: 0;
  display:flex;
  flex-direction: column;
  padding: 0;
}
.item {
  margin-top: 10px;
  transition: all linear .3s
}

</style>

动态拖拽交换位置——draggable组件

动态拖拽交换位置——vue-draggable-next组件(适用于vue3)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值