以前撸了一个 copy 指令,此次再撸一个拖拽指令。。css
具体是个什么蛇皮玩意儿呢,大概就像介样:html
因为咱们项目中用的是 element-ui
,全部这个指令只针对 element-ui
的对话框组件哈,若是大家用的别的ui
库也有这个需求的,涂涂改改应该也能用。。element-ui
其实这个拖拽的原理仍是很简单的:dom
onmousedown
)
left
和top
值onmousemove
)
disX
) 和纵向距离 (disY
)left
(left = left + disX
)和top
(top = top + disY
)值onmouseup
)
left
和 top
值容易获取,关键是 disX
和 disY
怎么计算呢?函数
容我先普及一哈:post
那么伪代码就是:3d
disX
= 鼠标按下时的 clientX
- 鼠标松开时的 clientX
code
disY
= 鼠标按下时的 clientY
- 鼠标松开时的 clientY
就这么简单,好了,下面就开始撸代码了。
// 这个助手方法下面会用到,用来获取 css 相关属性值
const getAttr = (obj, key) => (
obj.currentStyle
? obj.currentStyle[key]
: window.getComputedStyle(obj, false)[key]
);
const vDrag = {
inserted(el) {
/** * 这里是跟据 dialog 组件的 dom 结构来写的 * target: dialog 组件的容器元素 * header:dialog 组件的头部区域,也是就是拖拽的区域 */
const target = el.children[0];
const header = target.children[0];
// 鼠标手型
header.style.cursor = 'move';
header.onmousedown = (e) => {
// 记录按下时鼠标的坐标和目标元素的 left、top 值
const currentX = e.clientX;
const currentY = e.clientY
const left = parseInt(getAttr(target, 'left'));
const top = parseInt(getAttr(target, 'top'));
document.onmousemove = (event) => {
// 鼠标移动时计算每次移动的距离,并改变拖拽元素的定位
const disX = event.clientX - currentX;
const disY = event.clientY - currentY;
target.style.left = `${left + disX}px`;
target.style.top = `${top + disY}px`;
// 阻止事件的默认行为,能够解决选中文本的时候拖不动
return false;
}
// 鼠标松开时,拖拽结束
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
}
},
// 每次从新打开 dialog 时,要将其还原
update(el) {
const target = el.children[0];
target.style.left = '';
target.style.top = '';
},
// 最后卸载时,清除事件绑定
unbind(el) {
const header = el.children[0].children[0];
header.onmousedown = null;
},
};
export default vDrap;
复制代码
这样就实现了最简单的拖拽了,这样就 ok
了吗? 固然不是,这样会有什么问题呢?就是若是用力过猛把整个弹框都拖到可视区域以外了,那就抠不出来了。
因此还得完善一下,判断四个方向的边界,若是超过边界值就不动了。边界值实际上就是在屏幕上能拖动的最大距离也就是 disX
和 disY
的最大值
target.offsetTop
offsetTop
:这里能够表示目标元素(target
)上边框距离页面顶部的距离body.height - target.offsetTop - header.height
header.height
:预留高度,表示往下能够拖到只留下可拖拽区域在外面target.offsetLeft + target.width - 50
offsetLeft
:这里能够表示目标元素左边框距离页面左边的距离50
:表示预留的宽度,能够本身随便定只要大于 0
便可,表示往左再怎么拖也会留下 50px
的宽度在外面body.width - target.offsetLeft - 50
50
同上,表示往左再怎么拖也会留下 50px
的宽度在外面这里计算边界值的方法有多种,你们能够去尝试本身的想法。而后我粗略的画了一个图,帮助理解,虽然感受只有我本身看得懂。哈哈。。。
ok
了
// ...
// 以上代码省略
header.onmousedown = (e) => {
// ...
// 以上代码省略
// 分别计算四个方向的边界值
const minLeft = target.offsetLeft + parseInt(getAttr(target, 'width')) - 50;
const maxLeft = parseInt(getAttr(document.body, 'width')) - target.offsetLeft - 50;
const minTop = target.offsetTop;
const maxTop = parseInt(getAttr(document.body, 'height'))
- target.offsetTop - parseInt(getAttr(header, 'height'));
document.onmousemove = (event) => {
// 鼠标移动时计算每次移动的距离,并改变拖拽元素的定位
const disX = event.clientX - currentX;
const disY = event.clientY - currentY;
// 判断左、右边界
if (disX < 0 && disX <= -minLeft) {
target.style.left = `${left - minLeft)}px`;
} else if (disX > 0 && disX >= maxLeft) {
target.style.left = `${left + maxLeft}px`;
} else {
target.style.left = `${left + disX}px`;
}
// 判断上、下边界
if (disY < 0 && disY <= -minTop) {
target.style.top = `${top - minTop)}px`;
} else if (disY > 0 && disY >= maxTop) {
target.style.top = `${top + maxTop}px`;
} else {
target.style.top = `${top + disY}px`;
}
return false;
};
}
复制代码
这样注册以后就可使用了:
<el-dialog v-drag title="对话框" :visible.sync="dialogVisible"></el-dialog>
复制代码
这里就不介绍指令的钩子函数以及如何全局注册了,感兴趣的能够看看我上一篇文章。