本文主要记录 vue3 自定义指令的用法的一个案例,通过自定义指令实现一个盒子(类似弹窗)可以被鼠标在浏览器的可视区域内自由拖动:
下面直接上代码:(注:代码使用了 vue3 + ts 的写法)
template
<template>
<div v-move class="max-box">
<div class="header"></div>
<div class="content">
我是一个可以被鼠标拖动的盒子
</div>
</div>
</template>
typeScript
<script setup lang='ts'>
import { ref, Directive, DirectiveBinding } from 'vue';
// 定义一个自定义指令 v-move
const VMove: Directive<any, void> = (el: HTMLElement, bingding: DirectiveBinding) => {
console.log('el', el); // 这个 el 就是挂载 v-move 指定的元素
// 获取到盒子右侧和底部可以移动到的最值
const right = window.innerWidth - el.clientWidth
const bottom = window.innerHeight - el.clientHeight
console.log('盒子右下最值:', right, bottom);
// 获取内部第一个div元素
let moveElement: HTMLDivElement = el.firstElementChild as HTMLDivElement
console.log('获取的元素', moveElement); // 这里获取到的就是 header 所在的div元素
// 鼠标按下的事件
const mouseDown = (e: MouseEvent) => {
let X = e.clientX - el.offsetLeft
let Y = e.clientY - el.offsetTop
// 鼠标移动事件
const move = (e: MouseEvent) => {
// 控制向左移动最左不能超出窗口左侧 且 向右移动最右不能让盒子超出窗口右侧
let moveX = e.clientX - X < 0 ? 0 : ((e.clientX - X) > right ? right : e.clientX - X)
// 控制向上移动最上不能超出窗口顶部 且 向下移动最下不能让盒子超出窗口底部
let moveY = e.clientY - Y < 0 ? 0 : ((e.clientY - Y) > bottom ? bottom : e.clientY - Y)
el.style.left = moveX + 'px'
el.style.top = moveY + 'px'
}
document.addEventListener('mousemove', move)
// 在鼠标抬起时去移除移动事件
document.addEventListener('mouseup', () => {
// 移除移动事件
document.removeEventListener('mousemove', move)
})
}
// 为获取到的div盒子绑定鼠标按下事件
moveElement.addEventListener('mousedown', mouseDown)
}
</script>
style (less)
<style lang='less' scoped>
.max-box {
width: 250px;
min-height: 250px;
background-color: #fff;
position: absolute;
top: 30%;
left: 50%;
.header {
width: 100%;
height: 30px;
background-color: black;
cursor: move;
}
.content {
min-height: 250px;
border: 2px solid black;
border-top: 0;
padding: 10px;
}
}
</style>
效果图:
特点:内部限制了盒子上下左右移动的范围最值,保证盒子只能在浏览器可视区域内移动。