思路:
- 元素需要脱离文档流。设置为 position : absolute
- 鼠标点击时,获取点击的位置距离元素的左边和顶边的偏移量。
- 鼠标移动,是在父元素中移动,一般是 html,即 document.documentElement。注意不是在元素中移动。
- 获取 鼠标点击位置的客户端坐标值(clientX,clientY),根据偏移量计算出元素 style 属性的 left 和 top 值。
- 鼠标放开清除移动事件。
- 边界的处理。
- 上边界,左边界。判断 offsetTop 或者 offsetLeft 值 <= 0。相应地改变元素的位置。
- 下边界,右边界。
- 下边界。 offsetTop >= 元素可移动的区域 height - 元素自身的高度。
- 右边界。 offsetLeft >= 元素可移动的区域 width - 元素自身的宽度。
注意点
- 获取窗口的宽高用 document.documentElement.clientWidth || clientHeight。用 html 元素的 offsetHeight 和 offsetWidth 偏移量的话,会是 0,查看 dom 节点会发现其未被撑开。
代码实现
class Drag {
constructor(ele) {
this.ele = ele;
this.ele.onmousedown = this.onmousedown.bind(this);
}
onmousedown(e) {
e = e || window.event;
this.offsetX = e.clientX - this.ele.offsetLeft;
this.offsetY = e.clientY - this.ele.offsetTop;
document.documentElement.onmousemove = this.onmousemove.bind(this);
document.documentElement.onmouseup = this.onmouseup.bind(this);
// mousemove 事件只有在元素内部移动时才会触发
}
onmousemove(e) {
e = e || window.event;
let { offsetWidth, offsetHeight } = this.ele;
let { clientX, clientY } = e;
let curX = clientX - this.offsetX, curY = clientY - this.offsetY;
if (curX <= 0) {
curX = 0;
} else if (curX > document.documentElement.clientWidth - offsetWidth) {
curX = document.documentElement.clientWidth - offsetWidth;
}
if (curY <= 0) {
curY = 0;
} else if (curY > document.documentElement.clientHeight - offsetHeight) {
curY = document.documentElement.clientHeight - offsetHeight;
}
this.ele.style.left = curX + 'px';
this.ele.style.top = curY + 'px';
}
onmouseup() {
document.documentElement.onmousemove = null;
}
}