拖拽原理: 鼠标按下时记录初始坐标以及元素的初始位置,在鼠标移动时计算当前坐标与初始坐标的差值,这个差值就是元素所移动的距离,注意最后移动的时候需要加上元素的初始位置。
观察指令的钩子,我们只需要用到mounted。
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) {}
}
一个简单的拖拽指令
- clientX 和 clientY 是鼠标事件对象中的属性,用于表示鼠标相对于浏览器窗口(视口)的水平和垂直坐标位置。
- offsetLeft 和 offsetTop 是 DOM 元素的属性,用于表示该元素相对于其包含元素(父元素)的水平和垂直偏移量。换句话说,它们表示元素的位置,以像素为单位,相对于其父元素的左上角。在拖拽操作中,它们被用来记录元素的初始位置,以便在拖拽过程中计算新的位置。
定义几个变量,当调用 handleMouseDown 函数(鼠标按下元素时)时,将初始的鼠标坐标(event.clientX 和 event.clientY)以及元素的初始位置(el.offsetLeft 和 el.offsetTop)保存在 initialX、initialY、offsetX 和 offsetY 中。同时开启对鼠标移动和松开事件的监听。
import type { ObjectDirective } from 'vue';
const dragSimple: ObjectDirective<HTMLElement> = {
mounted(el) {
el.style.position = 'absolute';
el.style.zIndex = '999';
let initialX = 0;
let initialY = 0;
let offsetX = 0;
let offsetY = 0;
const handleMouseDown = (event: MouseEvent) => {
if (event.button !== 0)
return; // 检查是否是鼠标左键
initialX = event.clientX;
initialY = event.clientY;
offsetX = el.offsetLeft;
offsetY = el.offsetTop;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
const handleMouseMove = (event: MouseEvent) => {
const dx = event.clientX - initialX;
const dy = event.clientY - initialY;
el.style.left = `${offsetX + dx}px`;
el.style.top = `${offsetY + dy}px`;
};
const handleMouseUp = () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
el.addEventListener('mousedown', handleMouseDown);
},
};
export { dragSimple };
- 使用
<div v-drag-simple class="fixed-button">
<a-button type="primary" size="large" shape="circle" @click="visible = true">
<template #icon>
<Icon icon="setting-outlined " />
</template>
</a-button>
</div>
试了一下,拖拽非常的丝滑,没有问题,但是我们发现在拖拽结束的时候click事件被触发了,我和我们想要的效果不符,我们希望在拖拽的时候不要触发这个点击事件,只有在点击的时候才触发。下面提供两个解决方案。
使用阈值
设置一个阈值CLICK_THRESHOLD,当鼠标移动距离不超过阈值就认为是点击事件,超过则为拖拽事件。这种写法需要外部传递一个函数。
import type { ObjectDirective } from 'vue';
const CLICK_THRESHOLD = 5;
const dragSimple: ObjectDirective<HTMLElement> = {
mounted(el, { value }) {
el.style.position = 'absolute';
el.style.zIndex = '999';
let initialX = 0;
let initialY = 0;
let offsetX = 0;
let offsetY = 0;
let isDragging = false; // 添加一个标记,表示当前是否正在拖拽
const handleMouseMove = (event: MouseEvent) => {
const dx = event.clientX - initialX;
const dy = event.clientY - initialY;
el.style.left = `${offsetX + dx}px`;
el.style.top = `${offsetY + dy}px`;
// 判断是否开始拖拽
if (!isDragging) {
const distance = Math.sqrt(dx * dx + dy * dy); // 计算直角三角形的斜边长度
if (distance >= CLICK_THRESHOLD)
isDragging = true;
// 在阈值外触发拖拽事件
// 这里可以触发拖拽事件的回调函数
}
};
const handleMouseUp = () => {
document.removeEventListener('mousemove', handleMouseMove);
el.removeEventListener('mouseup', handleMouseUp); // 移除mouseup事件监听器
if (!isDragging) {
// 在阈值内触发点击事件
// 这里可以触发点击事件的回调函数
value();
}
isDragging = false; // 重置拖拽标记
};
const handleMouseDown = (event: MouseEvent) => {
if (event.button !== 0)
return; // 检查是否是鼠标左键
initialX = event.clientX;
initialY = event.clientY;
offsetX = el.offsetLeft;
offsetY = el.offsetTop;
document.addEventListener('mousemove', handleMouseMove);
el.addEventListener('mouseup', handleMouseUp); // 将mouseup事件监听器添加到元素el上
};
el.addEventListener('mousedown', handleMouseDown);
},
};
export { dragSimple };
- 使用
<div v-drag-simple="() => visible = true" class="fixed-button">
<a-button type="primary" size="large" shape="circle">
<template #icon>
<Icon icon="setting-outlined " />
</template>
</a-button>
</div>
直接使用pointer-events
有一种更简便的方法,就是在鼠标移动的时候取消元素的 pointer-events,在鼠标松开的时候再恢复。
import type { ObjectDirective } from 'vue';
const dragSimple: ObjectDirective<HTMLElement> = {
mounted(el) {
el.style.position = 'absolute';
el.style.zIndex = '999';
let initialX = 0;
let initialY = 0;
let offsetX = 0;
let offsetY = 0;
const handleMouseDown = (event: MouseEvent) => {
if (event.button !== 0)
return; // 检查是否是鼠标左键
initialX = event.clientX;
initialY = event.clientY;
offsetX = el.offsetLeft;
offsetY = el.offsetTop;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
const handleMouseMove = (event: MouseEvent) => {
const dx = event.clientX - initialX;
const dy = event.clientY - initialY;
el.style.pointerEvents = 'none'; // 取消元素的 pointer-events,防止拖拽过程中触发子元素的事件
el.style.left = `${offsetX + dx}px`;
el.style.top = `${offsetY + dy}px`;
};
const handleMouseUp = () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
el.style.pointerEvents = ''; // 恢复元素的 pointer-events
};
el.addEventListener('mousedown', handleMouseDown);
},
};
export { dragSimple };
加个小功能,刷新后还能保持位置
/* eslint-disable @typescript-eslint/no-use-before-define */
import type { ObjectDirective } from 'vue';
const dragSimple: ObjectDirective<HTMLElement> = {
mounted(el) {
el.style.position = 'absolute';
el.style.zIndex = '999';
let initialX = 0;
let initialY = 0;
let offsetX = 0;
let offsetY = 0;
const POSITION_STORAGE_KEY = 'dragPosition';
const handleMouseDown = (event: MouseEvent) => {
if (event.button !== 0)
return; // 检查是否是鼠标左键
initialX = event.clientX;
initialY = event.clientY;
offsetX = el.offsetLeft;
offsetY = el.offsetTop;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
const handleMouseMove = (event: MouseEvent) => {
const dx = event.clientX - initialX;
const dy = event.clientY - initialY;
el.style.pointerEvents = 'none'; // 取消元素的 pointer-events,防止拖拽过程中触发子元素的事件
el.style.left = `${offsetX + dx}px`;
el.style.top = `${offsetY + dy}px`;
// 保存位置信息到 localStorage
const positionInfo = {
left: el.style.left,
top: el.style.top,
};
localStorage.setItem(POSITION_STORAGE_KEY, JSON.stringify(positionInfo));
};
const handleMouseUp = () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
el.style.pointerEvents = ''; // 恢复元素的 pointer-events
};
el.addEventListener('mousedown', handleMouseDown);
// 页面加载时,从 localStorage 中恢复位置信息
const storedPositionInfo = localStorage.getItem(POSITION_STORAGE_KEY);
if (storedPositionInfo) {
const { left, top } = JSON.parse(storedPositionInfo);
el.style.left = left ?? 0;
el.style.top = top ?? 0;
}
},
};
export { dragSimple };
文章结束啦~
本文暂时没有评论,来添加一个吧(●'◡'●)