由于自定义指令可以获取到真实DOM元素,因此我们可以在自定义指令中为元素绑定各种原生事件,这里以拖拽盒子为案例,需要注意以下几点:
- 拖拽盒子会用到left、top之类的属性,只有在开启了定位的元素身上才会存在(相对、绝对、固定等)
- 要将鼠标移动事件绑定到要移动的整个大盒子身上,只绑定小盒子会出现各种意外(本例中点击head,但将mousemove添加到了document身上)
- 如果直接将left设置为e.clientX,将top设置为e.clientY则盒子默认左上角会跑到鼠标当前所在的位置。此时需要将e.clientX和e.clientY进行回减才能让鼠标在盒子上的位置不变。回减量为let 水平回减量 = e.clientX - el.offsetLeft,let 垂直回减量 = e.clientY - el.offsetTop,最后将e.clientX - 水平回减量,e.clientY - 垂直回减量即可
- 最后,由于需要移除事件,addEventListener中的回调必须额外定义,不能直接写在调用参数列表中
完整代码:
App.vue
<template>
<div v-move class="box">
<div class="head"></div>
<div>内容</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, Directive, DirectiveBinding } from 'vue'
const vMove: Directive = (el: HTMLElement, binding: DirectiveBinding) => {
let moveElement = <HTMLElement>el.firstElementChild
const mouseDown = (e: MouseEvent) => {
// 算出回减量
let X = e.clientX - el.offsetLeft
let Y = e.clientY - el.offsetTop
// 减去回减量
const move = (e: MouseEvent) => {
el.style.left = e.clientX - X + 'px'
el.style.top = e.clientY - Y + 'px'
}
document.addEventListener('mousemove', move)
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', move)
})
}
moveElement.addEventListener('mousedown', mouseDown)
}
</script>
<style scoped>
.box {
position: fixed;
width: 200px;
height: 300px;
border: 1px solid black;
}
.head {
width: 100%;
height: 30px;
background-color: black;
}
</style>