需求是聊天框图标可以在页面范围内上下被拖拽且不能超出页面范围,并且在拖拽的同时不能触发按钮的点击事件,单独点击时才打开弹框
#html
<div class="groundChat" v-draggable></div>
#指令
directives: {//自定义指令,拖拽
draggable: {
bind: function (el, binding, vnode) {
el.style.position = 'fixed'; // 设置元素为 fixed 定位
const _el = el; // 获取当前元素
const ref = vnode.context.$refs[binding.value]; // 判断基于移动的是哪一个盒子
const masterNode = ref ? ref : document; // 用于绑定事件
const masterBody = ref ? ref : document.body; // 用于获取高和宽
const maxHeight = masterBody.clientHeight; // 窗口可见区域的高度
const elHeight = _el.clientHeight; // 元素自身的高度
let disX = 0, disY = 0; // 鼠标按下时的偏移量
let isDragging = false; // 是否正在拖动的标志
let isClick = false; // 是否是点击
function handleMouseDown(e) { // 鼠标落下
if (isDragging) { // 如果已经在拖动中,则直接返回
return;
}
e.preventDefault(); // 阻止默认事件,例如文本框的选中
isDragging = true; // 设置正在拖动的标志为 true
isClick = true; // 设置点击标志为 true
// disX = e.clientX - _el.offsetLeft; // 计算鼠标按下时的横向偏移量
disY = e.clientY - _el.offsetTop; // 计算鼠标按下时的纵向偏移量
masterNode.addEventListener('mousemove', dragElement); // 绑定鼠标移动事件
masterNode.addEventListener('mouseup', handleMouseUp); // 绑定鼠标松开事件
// 监听窗口的 resize 事件,重新获取可见区域的宽度和高度
window.addEventListener('resize', handleResize);
}
function dragElement(e) { // 拖拽的计算
if (isDragging) { // 如果正在拖动,则执行拖动操作
let top = e.clientY - disY; // 计算拖动后的纵向偏移量
top = Math.min(top, maxHeight - elHeight - 70); // 这里使用 Math.min 来确保不超出底部边界,70是元素的高度要计算进去
top = Math.max(top, 0); // 使用 Math.max 确保不超出顶部边界
_el.style.top = top + 'px'; // 设置元素的纵向位置
isClick = false; // 拖拽的时候设置 isClick 为 false,不让触发打开弹框的事件
}
}
function handleMouseUp() { // 鼠标松开
isDragging = false; // 设置正在拖动的标志为 false
masterNode.removeEventListener('mousemove', dragElement); // 解绑鼠标移动事件
masterNode.removeEventListener('mouseup', handleMouseUp); // 解绑鼠标松开事件
window.removeEventListener('resize', handleResize); // 解绑窗口 resize 事件
}
function handleResize() { // 窗口 resize 事件处理程序
maxWidth = masterBody.clientWidth; // 更新可见区域宽度
maxHeight = masterBody.clientHeight; // 更新可见区域高度
}
_el.addEventListener('mousedown', handleMouseDown); // 绑定鼠标按下事件
// 点击事件处理程序
vnode.context.handleClick = e => {
if (!isDragging && isClick) { // 如果不是拖动状态且是点击操作,则执行相应的逻辑
vnode.context.showTips(); // 调用组件实例的 showTips 方法
}
};
},
inserted(el, binding, vnode) {
el.addEventListener('click', vnode.context.handleClick); // 绑定点击事件
},
unbind(el, binding, vnode) {
el.removeEventListener('click', vnode.context.handleClick); // 解绑点击事件
}
}
},