likes
comments
collection
share

Vue 自定义指定:拖拽

作者站长头像
站长
· 阅读数 287

前言

被推拽元素,与其父元素,须保持子绝父相,或子绝父绝。

案例

<!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 v-for="item in box_count" :key="item">{{ item }}</div>
    </div>

    <script>
      // 设置为 false 以阻止 Vue 在启动时生成生产提示
      Vue.config.productionTip = false

      // new 一个 Vue 实例,并挂载到 #app 上
      new Vue({
        el: '#app',
        data() {
          return {
            box_count: 9, // 控制页面中有几个可以拖拽的盒子
          }
        },
        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>