【Vue3】24-自定义指令实现拖拽

52 阅读1分钟
<template>
<div v-drag class="box">
    <h2>标题</h2>
    <section>内容</section>
</div>
</template>

<script setup lang="ts">
import { Directive, DirectiveBinding } from 'vue';

const vDrag: Directive<any, void> = (el: HTMLElement, binding: DirectiveBinding) => {

    // 鼠标点击事件的回调
    const mouseDown = (e: MouseEvent) => {
        let X = e.clientX - el.offsetLeft
        let Y = e.clientY - el.offsetTop

        const mouseMove = (e: MouseEvent) => {
            el.style.left = e.clientX - X + 'px'
            // 水平边界判断
            if(el.offsetLeft < 0)
                el.style.left = 0 + 'px'
            if(el.offsetLeft > document.documentElement.clientWidth - el.offsetWidth)
                el.style.left = document.documentElement.clientWidth - el.offsetWidth + 'px'

            // 垂直边界判断
            el.style.top = e.clientY - Y + 'px'
            if(el.offsetTop < 0)
                el.style.top = 0 + 'px'
            if(el.offsetTop > document.documentElement.clientHeight - el.offsetHeight)
                el.style.top = document.documentElement.clientHeight - el.offsetHeight + 'px'
        }

        // 注意以下是用 document,而不是 el 本身
        // 这是为了防止 el 跟不上鼠标移动的速度从而导致鼠标脱离了 el 但又没有释放

        // 添加鼠标移动事件
        document.addEventListener('mousemove', mouseMove)

        // 添加鼠标松开事件
        document.addEventListener('mouseup', () => {
            document.removeEventListener('mousemove', mouseMove)
        })

    }

    // 添加鼠标按下事件
    el.addEventListener('mousedown', mouseDown)
}

</script>

<style scoped>
.box {
    width: 300px;
    height: 250px;
    border: 2px solid #ccc;
    border-top: 50px solid #333;
    text-align: center;
    background-color: #eee;
    /* 注意:一定要有定位,不然 left 和 top 不起作用 */
    position: fixed;  
}
</style>