由上述效果图可以看到,我们一共定义了8个可拖拽的点,分别为上下左右四个角和每条线的中间四个点,在这里,我们要给每个点一个标识,来区分他们的位置。
- 我们定义水平方向的标识为”horizental“,垂直方向的标识为”vertical“,
- 左上角顶点定义为”start“, 水平方向上三个点依次为"start", "center", "end", 垂直方向上三个点也依次为"start", "center", "end", 则我们可以由以上的标识定义出来8个点的位置。
<div class="container" id="container">
<!-- 左中 -->
<div class="block-resize block-resize-left" onmousedown="handleMouseDown(event, { horizental: 'start', vertical: 'center' })"></div>
<!-- 右中 -->
<div class="block-resize block-resize-right" onmousedown="handleMouseDown(event, { horizental: 'end', vertical: 'center' })"></div>
<!-- 上中 -->
<div class="block-resize block-resize-top" onmousedown="handleMouseDown(event, { horizental: 'center', vertical: 'start' })"></div>
<!-- 下中 -->
<div class="block-resize block-resize-bottom" onmousedown="handleMouseDown(event, { horizental: 'center', vertical: 'end' })"></div>
<!-- 左上 -->
<div class="block-resize block-resize-top-left" onmousedown="handleMouseDown(event, { horizental: 'start', vertical: 'start' })"></div>
<!-- 右上 -->
<div class="block-resize block-resize-top-right" onmousedown="handleMouseDown(event, { horizental: 'end', vertical: 'start' })"></div>
<!-- 左下 -->
<div class="block-resize block-resize-bottom-left" onmousedown="handleMouseDown(event, { horizental: 'start', vertical: 'end' })"></div>
<!-- 右下 -->
<div class="block-resize block-resize-bottom-right" onmousedown="handleMouseDown(event, { horizental: 'end', vertical: 'end' })"></div>
</div>
点的位置定义出来之后,这里我们大概给每个点加个样式。
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
display: flex;
}
.container {
position: absolute;
width: 100px;
height: 100px;
/* margin: auto; */
background-color: lightcoral;
left: 20%;
top: 40%;
}
.block-resize {
position: absolute;
width: 8px;
height: 8px;
background-color: #409eff;
user-select: none;
z-index: 1000;
}
.block-resize-top {
top: -4px;
left: calc(50% - 4px);
}
.block-resize-bottom {
bottom: -4px;
left: calc(50% - 4px);
}
.block-resize-left {
top: calc(50% - 4px);
left: -4px
}
.block-resize-right {
top: calc(50% - 4px);
right: -4px;
}
.block-resize-top-left {
left: -4px;
top: -4px;
}
.block-resize-top-right {
right: -4px;
top: -4px;
}
.block-resize-bottom-left {
left: -4px;
bottom: -4px;
}
.block-resize-bottom-right {
right: -4px;
bottom: -4px;
}
</style>
到这里,我们已经可以看到上面截图的效果,下面我们来实现功能。
首先,当鼠标摁下的时候,我们需要记录当前状态下的一些信息点。我们定义一个data变量来存储这些信息。
let data = {};
let container = document.getElementById('container');
function handleMouseDown (e, direction) {
e.stopPropagation();
data = {
startX: e.clientX,
startY: e.clientY,
startWidth: container.offsetWidth,
startHeight: container.offsetHeight,
startLeft: container.offsetLeft,
startTop: container.offsetTop,
direction
}
document.body.addEventListener('mousemove', handleMousemove)
document.body.addEventListener('mouseup', handleMouseup)
}
鼠标移动过程中,我们需要计算拖动的距离,这里有两个需要注意的点:
- 当我们拖动中间点的时候,要么是想上下调整,要么是要左右调整,但是拖动过程中,我们避免不了鼠标会有左右偏差,所以这里我们需要对中间的点做一些额外处理已达到我们想要的效果。
- 当我们反向拖拽的时候,对拖动的距离需要取反,以拿到正确的组件left和top值。
function handleMousemove(e) {
let { clientX, clientY } = e;
let { startX, startY, startWidth, startHeight, startLeft, startTop, direction } = data;
if(direction.horizental === 'center') { // 如果拖拽的是中间的点 x轴是不变的
clientX = startX;
}
if(direction.vertical === 'center') { // 只能改横向 不能改纵向
clientY = startY;
}
let durX = clientX - startX;
let durY = clientY - startY;
// 针对反向拖拽 需要取反 拿到正确的组件left和top
if(direction.horizental === 'start') {
durX = -durX;
container.style.left = startLeft - durX + 'px';
}
if(direction.vertical === 'start') {
durY = -durY;
container.style.top = startTop - durY + 'px';
}
container.style.width = startWidth + durX + 'px';
container.style.height = startHeight + durY + 'px';
}
最后,我们需要在鼠标抬起的时候,移除监听函数。
function handleMouseup() {
document.body.removeEventListener('mousemove', handleMousemove)
document.body.removeEventListener('mouseup', handleMouseup)
}