由于在需求过程中。需要实现一个页面的上下左右拖动效果。看到demo大部份是左右拖动。并且不是完全的封装。都因此在前人的代码基础上去实现一个vue2的拖动布局组件,通过插槽无缝嵌入
实现效果如下
布局逻辑
- 使用
<div>
元素创建了一个名为dragBox
的容器,它包含三个主要部分:左侧面板(dragBox-left
)、拖动条(drag
)和右侧面板(dragBox-right
)。 - 通过
slot
标签提供了插槽,允许用户自定义左右面板的内容。
js
<template>
<div ref="dragBox" class="dragBox" :class="dragBoxClass">
<div class="dragBox-left" ref="dragBox_left">
<slot name="left"></slot>
</div>
<div ref="drag" class="drag" :class="dragClass">
<span v-for="item in 3" :key="item"></span>
</div>
<div class="dragBox-right" ref="dragBox_right">
<slot name="right"></slot>
</div>
</div>
</template>
拖动逻辑思路
-
鼠标按下事件 (mousedown) :当用户按下鼠标时,记录当前鼠标位置并开始拖动。
-
鼠标移动事件 (mousemove) :当用户拖动鼠标时,根据鼠标的移动距离调整对应的区域大小。
-
鼠标松开事件 (mouseup) :当用户松开鼠标时,停止拖动并清理事件。
1. 鼠标按下事件 (mousedown)
当用户按下拖动控件时,我们需要记录鼠标的初始位置,并设置拖动控件的初始偏移位置。以下是纵向拖动的实现代码:
js
dragControllerUpDown() {
let resize = this.$refs.drag;
let left = this.$refs.dragBox_left;
let right = this.$refs.dragBox_right;
let box = this.$refs.dragBox;
resize.onmousedown = function (e) {
// 改变拖动控件的颜色提示用户正在拖动
resize.style.background = '#818181';
// 记录鼠标的初始位置
let startY = e.clientY;
// 记录拖动控件的初始偏移位置
resize.top = resize.offsetTop;
// 鼠标拖动事件
document.onmousemove = function (e) {
// 计算鼠标移动的距离
let endY = e.clientY;
let moveLen = resize.top + (endY - startY);
// 获取容器的最大高度,防止拖动超出范围
let maxT = box.clientHeight - resize.offsetHeight;
if (moveLen < 200) moveLen = 200; // 设置上边区域的最小高度
if (moveLen > maxT - 200) moveLen = maxT - 200; // 设置下边区域的最小高度
// 更新拖动控件和左右区域的高度
resize.style.top = moveLen;
left.style.height = moveLen + 'px';
right.style.height = box.clientHeight - moveLen - 10 + 'px';
};
// 鼠标松开事件
document.onmouseup = function () {
// 恢复拖动控件的颜色
resize.style.background = '#d6d6d6';
// 移除鼠标移动和松开事件
document.onmousemove = null;
document.onmouseup = null;
// 释放鼠标捕获
resize.releaseCapture && resize.releaseCapture();
};
// 捕获鼠标事件
resize.setCapture && resize.setCapture();
return false;
};
}
2. 鼠标移动事件 (mousemove)
在 mousemove
事件中,我们根据鼠标的移动距离来调整左右或上下区域的大小。以下是详细的逻辑解释:
- 计算鼠标移动的距离 :
endY - startY
计算出鼠标移动的距离。 - 计算新的控件位置 :
resize.top + (endY - startY)
计算出拖动控件的新位置。 - 防止超出边界:通过设置最小和最大高度/宽度,防止拖动超出容器的边界。
- 更新高度/宽度:根据新的控件位置,更新左右或上下区域的高度/宽度。
3. 鼠标松开事件 (mouseup)
在 mouseup
事件中,我们需要停止拖动并清理事件,具体步骤如下:
- 恢复拖动控件的颜色:提示用户拖动已结束。
- 移除
mousemove
和mouseup
事件:停止拖动逻辑。 - 释放鼠标捕获:使得控件不再捕获鼠标事件。
横向拖动逻辑
横向拖动逻辑与纵向类似,只是坐标从 Y 轴改为 X 轴,高度改为宽度:
添加监听事件window的resize事件。实时获取盒子的宽或高
js
window.addEventListener('resize', this.GetDragscreen);
GetDragscreen() {
//获取当前dragBox的高度或宽度(根据你当前的type来决定)
if (this.DragType == 'column') {
return this.$refs.dragBox.clientHeight;
} else if (this.DragType == 'row') {
return this.$refs.dragBox.clientWidth;
}
},
完整代码
js
<template>
<div ref="dragBox" class="dragBox" :class="dragBoxClass">
<div class="dragBox-left" ref="dragBox_left">
<slot name="left"></slot>
</div>
<div ref="drag" class="drag" :class="dragClass">
<span v-for="item in 3" :key="item"></span>
</div>
<div class="dragBox-right" ref="dragBox_right">
<slot name="right"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'drag',
props: {
DragType: {
type: String,
default: 'column',
},
},
computed: {
dragBoxClass() {
return {
DragUpDown: this.DragType === 'column',
DragLeftRight: this.DragType === 'row',
};
},
dragClass() {
return {
'DragLeftRight-drag': this.DragType === 'row',
'DragUpDown-drag': this.DragType === 'column',
};
},
},
data() {
return {
screenWidth: 0, // 获取当前dragBox的宽度或高度
};
},
mounted() {
this.GetDragscreen();
this.dragControllerDiv();
window.addEventListener('resize', this.GetDragscreen);
},
beforeDestroy() {
window.removeEventListener('resize', this.GetDragscreen);
},
watch: {
screenWidth(newVal, oldVal) {
this.DragType == 'column' && this.watchDragUpDown(newVal);
this.DragType == 'row' && this.watchDragLeftRight(newVal);
},
},
methods: {
watchDragUpDown(newVal) {
//在拖动中一直更改右边的高度
let resize = this.$refs.drag;
let right = this.$refs.dragBox_right;
right.style.height = newVal - resize.offsetTop + 'px';
},
watchDragLeftRight(newVal) {
//在拖动中一直更改div的高度
let resize = this.$refs.drag;
let right = this.$refs.dragBox_right;
right.style.width = newVal - resize.offsetLeft + 'px';
},
setDragscreen() {
//设置当前dragBox的高度或宽度(根据你当前的type来决定)
if (this.DragType == 'column') {
this.screenWidth = this.$refs.dragBox.clientHeight;
} else if (this.DragType == 'row') {
this.screenWidth = this.$refs.dragBox.clientWidth;
}
},
GetDragscreen() {
//获取当前dragBox的高度或宽度(根据你当前的type来决定)
if (this.DragType == 'column') {
return this.$refs.dragBox.clientHeight;
} else if (this.DragType == 'row') {
return this.$refs.dragBox.clientWidth;
}
},
dragControllerDiv() {
this.DragType == 'column' && this.dragControllerUpDown();
this.DragType == 'row' && this.dragControllerLeftRight();
},
dragControllerUpDown() {
let resize = this.$refs.drag;
let left = this.$refs.dragBox_left;
let right = this.$refs.dragBox_right;
let box = this.$refs.dragBox;
// 鼠标按下事件
resize.onmousedown = function (e) {
//颜色改变提醒
resize.style.background = '#818181';
let startX = e.clientY;
resize.top = resize.offsetTop;
// 鼠标拖动事件
document.onmousemove = function (e) {
let endX = e.clientY;
let moveLen = resize.top + (endX - startX); // (endx-startx)=移动的上下距离。
//resize.left+移动的距离=左边区域最后的宽度
let maxT = box.clientHeight - resize.offsetHeight; // 容器高度 - 左边区域的宽度 = 右边区域的宽度
if (moveLen < 200) moveLen = 200; // 上边区域的最小高度为200
if (moveLen > maxT - 200) moveLen = maxT - 200; //右边区域最小高度为200
resize.style.top = moveLen; // 设置上边区域的宽度
left.style.height = moveLen + 'px';
right.style.height = box.clientHeight - moveLen - 10 + 'px';
};
// 鼠标松开事件
document.onmouseup = function (evt) {
//颜色恢复
resize.style.background = '#d6d6d6';
document.onmousemove = null;
document.onmouseup = null;
resize.releaseCapture && resize.releaseCapture(); //当你不在需要继续获得鼠标消息就要应该调用ReleaseCapture()释放掉
};
resize.setCapture && resize.setCapture(); //该函数在属于当前线程的指定窗口里设置鼠标捕获
return false;
};
},
dragControllerLeftRight() {
let resize = this.$refs.drag;
let left = this.$refs.dragBox_left;
let right = this.$refs.dragBox_right;
let box = this.$refs.dragBox;
// 鼠标按下事件
resize.onmousedown = function (e) {
//颜色改变提醒
resize.style.background = '#818181';
let startX = e.clientX;
resize.left = resize.offsetLeft;
// 鼠标拖动事件
document.onmousemove = function (e) {
let endX = e.clientX;
let moveLen = resize.left + (endX - startX); // (endx-startx)=移动的左右距离。
//resize.left+移动的距离=左边区域最后的宽度
let maxT = box.clientWidth - resize.offsetWidth; // 容器宽度 - 左边区域的宽度 = 右边区域的宽度
if (moveLen < 200) moveLen = 200; // 左边区域的最小宽度为200
if (moveLen > maxT - 200) moveLen = maxT - 200; //右边区域最小宽度为200
resize.style.left = moveLen; // 设置左侧区域的宽度
left.style.width = moveLen + 'px';
right.style.width = box.clientWidth - moveLen - 10 + 'px';
};
// 鼠标松开事件
document.onmouseup = function (evt) {
//颜色恢复
resize.style.background = '#d6d6d6';
document.onmousemove = null;
document.onmouseup = null;
resize.releaseCapture && resize.releaseCapture(); //当你不在需要继续获得鼠标消息就要应该调用ReleaseCapture()释放掉
};
resize.setCapture && resize.setCapture(); //该函数在属于当前线程的指定窗口里设置鼠标捕获
return false;
};
},
beforeDestroy() {
window.onresize = null;
},
},
};
</script>
<style lang="less" scoped>
/*拖拽区div样式*/
.DragLeftRight-drag {
margin: 10px;
width: 10px;
height: 50px;
top: calc(50% - 25px);
font-size: 32px;
flex-direction: column;
align-items: center;
justify-content: center;
> span {
margin-top: 3px;
margin-bottom: 3px;
}
}
.DragUpDown-drag {
width: 50px;
height: 10px;
left: calc(50% - 40px);
margin: 10px;
align-items: center;
justify-content: center;
> span {
margin-left: 3px;
margin-right: 3px;
}
}
.drag {
display: flex;
border-radius: 5px;
cursor: col-resize;
position: relative;
background-color: #d6d6d6;
background-size: cover;
background-position: center;
> span {
width: 5px;
height: 5px;
display: block;
background: #fff;
border-radius: 50%;
}
}
/*拖拽区鼠标悬停样式*/
.drag:hover {
color: #444444;
}
.dragBox {
display: flex;
width: 100%;
height: 100%;
}
.DragUpDown {
flex-direction: column;
> .dragBox-left,
> .dragBox-right {
width: 100%;
height: 50%;
overflow: hidden;
}
}
.DragLeftRight {
flex-direction: row;
> .dragBox-left,
> .dragBox-right {
width: 50%;
height: 100%;
overflow: hidden;
}
}
</style>
```