vue3自定义指令,实现dom拖拽效果。

136 阅读2分钟

什么是自定义指令Directive

Vue 内置的一系列指令 (比如 `v-model` 或 `v-show`) 等常用的指令。
Vue 还允许你注册自定义的指令 (Custom Directives)。


一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。
钩子函数会接收到指令所绑定元素作为其参数。

指令钩子 (指令的生命周期)

const myDirective = { 
    // 在绑定元素的 attribute 前 
    // 或事件监听器应用前调用 
    created(el, binding, vnode, prevVnode) { 
        // 下面会介绍各个参数的细节 
     }, 
    // 在元素被插入到 DOM 前调用 
     beforeMount(el, binding, vnode, prevVnode) {},
     // 在绑定元素的父组件 
     // 及他自己的所有子节点都挂载完成后调用 
     mounted(el, binding, vnode, prevVnode) {},
     // 绑定元素的父组件更新前调用 
     beforeUpdate(el, binding, vnode, prevVnode) {},
     // 在绑定元素的父组件 
     // 及他自己的所有子节点都更新后调用 
     updated(el, binding, vnode, prevVnode) {}, 
     // 绑定元素的父组件卸载前调用 
     beforeUnmount(el, binding, vnode, prevVnode) {}, 
     // 绑定元素的父组件卸载后调用 
     unmounted(el, binding, vnode, prevVnode) {} 
    }

创建一个拖拽指令

function dragable(el: HTMLElement) {
    el.style.zIndex = '9999'
    el.style.position = 'relative'
    el.style.top = '0px'
    el.style.left = '0px'
    el.onmousedown = (ev) => {
        let { width, height, x, y } = el.getBoundingClientRect()
        let mouseX = ev.pageX
        let mouseY = ev.pageY
        let elTop = Number(el.style.top.split('px')[0])
        let elLeft = Number(el.style.left.split('px')[0])
        let clientWidth =  document.body.clientWidth
        let clientHeight = document.body.clientHeight

        document.onmousemove = (ev) => {
            let distanceWidth = ev.pageX - mouseX
            let distanceHeight = ev.pageY - mouseY
            // 边界测试
            // 左边界 
            if (x + distanceWidth <= 0) {
                distanceWidth = -x
            }
            // 右边界
            if (x + width + distanceWidth >= clientWidth ) {
                distanceWidth = clientWidth - width - x
            }
            // 上边界 
            if (y + distanceHeight <= 0) {
                distanceHeight = -y
            }
            // 下边界
            if (y + height + distanceHeight >= clientHeight ) {
                distanceHeight = clientHeight - height - y
            }
            el.style.left = (elLeft + distanceWidth) + 'px';
            el.style.top = (elTop + distanceHeight) + 'px'
        }
        document.onmouseup = (ev) => {
            document.onmousemove = null
            document.onmouseup = null
        }
    }
}
export default {
    name: "dragable",
    mounted(el: HTMLElement, binding: any) {
         // binging.value
        dragable(el)
    }
} 

局部注册

方式一

<template>
  <div class="test" v-dragable>这是可以拖拽的内容</div>
</template>

<script setup lang="ts">
import dragable from "../directive/dragable";
const vDragable = dragable;
</script>

方式二

<template>
  <div class="test" v-dragable>这是可以拖拽的内容</div>
</template>

<script lang="ts">
import dragable from "../directive/dragable";

export default {
  directives: {
    // 在模板中启用 v-dragable
    dragable,
  },
  setup() {},
};
</script>

全局注册方式

我们可以把所有的自定义指令放在一个目录里并创建一个入口函数

 // dirrectives.js
import dragable from "./dragable.js"
import lazyload from './lazyload.js'
let directivesArr = [dragable,lazyload]

export default {
    install(app, options) {
        directivesArr.forEach(item=>{
            app.directive(item.name, item)
        })
    }
}

在main.ts中注册指令

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
import directives from './directive'

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

使用

<template>
  <div class="test">
    <div class="p1" v-dragable>p1</div>
  </div>
  <!-- <img v-lazyload="imgSrc" @click="changeIt" > -->
</template>