前言

被推拽元素,与其父元素,须放弃子绝父相,或子绝父绝。

案例

<!DOCTYPE html><html lang="zh-CN">  <head>    <meta charset="UTF-8" />    <meta http-equiv="X-UA-Compatible" content="IE=edge" />    <meta name="viewport" content="width=device-width, initial-scale=1.0 , maximum-scale=1, minimum-scale=1, user-scalable=no" />    <title>Vue 自定义指令:拖拽</title>    <link rel="shortcut icon" href="https://gw.alipayobjects.com/mdn/prod_resou/afts/img/A*CUIoT4xopNYAAAAAAAAAAABkARQnAQ" type="image/x-icon" />    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.js"></script>    <style>      * {        margin: 0;        padding: 0;        overflow: hidden;        box-sizing: border-box;      }      #app {        width: 100vw;        height: 100vh;        overflow: hidden;      }      .box {        display: flex;        justify-content: center;        align-items: center;        position: absolute;        width: 100px;        height: 100px;        border-radius: 10px;        color: #fff;        background-color: cadetblue;        font-size: 60px;        font-weight: bold;      }    </style>  </head>  <body>    <div id="app">      <div class="box" v-drag>1</div>      <div class="box" v-drag>2</div>      <div class="box" v-drag>3</div>      <div class="box" v-drag>4</div>      <div class="box" v-drag>5</div>      <div class="box" v-drag>6</div>      <div class="box" v-drag>7</div>      <div class="box" v-drag>8</div>      <div class="box" v-drag>9</div>    </div>    <script>      // 设置为 false 以阻止 Vue 在启动时生成生产提醒      Vue.config.productionTip = false      // new 一个 Vue 实例,并挂载到 #app 上      new Vue({        el: '#app',        data() {          return {}        },        directives: {          // 对象写法,利用 inserted 钩子确保元素曾经被插入 DOM 时才执行          drag: {            inserted(el) {              el.onmousedown = (event) => {                const parentEl = el.parentElement                // 避免选中拖拽导致登程浏览器的搜寻事件                event.preventDefault()                // 获取鼠标与拖拽盒子的偏移量(确保鼠标始终在点击盒子时的地位)                const boxX = event.clientX - el.offsetLeft                const boxY = event.clientY - el.offsetTop                // 计算极限偏移量                const maxX = parentEl.offsetWidth - el.offsetWidth                const maxY = parentEl.offsetHeight - el.offsetHeight                // 获取拖拽元素的初始定位层级                const el_style = window.getComputedStyle(el, null)                const origin_zIndex = el_style.zIndex                const origin_cursor = el_style.cursor                el.style.zIndex = '9999'                el.style.cursor = 'move'                document.onmousemove = (event) => {                  // 获取鼠标以后的地位                  const mouseX = event.clientX                  const mouseY = event.clientY                  // 计算被拖拽盒子的偏移量                  let moveX = mouseX - boxX                  let moveY = mouseY - boxY                  // 限度盒子的推拽范畴                  moveX < 0 && (moveX = 0)                  moveY < 0 && (moveY = 0)                  moveX > maxX && (moveX = maxX)                  moveY > maxY && (moveY = maxY)                  // 赋予待拖拽的盒子新地位                  el.style.left = moveX + 'px'                  el.style.top = moveY + 'px'                }                document.onmouseup = (event) => {                  // 还原初始层级 && 鼠标款式                  el.style.zIndex = origin_zIndex                  el.style.cursor = origin_cursor                  document.onmousemove = null                  document.onmouseup = null                }              }            },          },        },      })    </script>  </body></html>