在开发过程中有时候会碰到需要拖拽修改页面元素宽度的需求,实际场景是页面上有并排两个或多个元素,需要拖拽修改其中一个元素的宽度,则只引入下方组件代替页面中需要被拖拽的元素及右侧相邻的元素。需要将需要拖拽的元素放进下面组件的左边的插槽中,右侧相邻的元素放入下方组件右侧的插槽中即可。
<template>
<!-- 左侧元素 -->
<el-aside
ref="leftAsideRef"
class="container__left border-right"
width="260px"
@mousemove="handleMousemove"
@mousedown="handleMousedown"
@mouseup="handleMouseup"
>
<slot name="left"></slot>
</el-aside>
<!-- 右侧元素 -->
<el-aside
ref="rightAsideRef"
class="container__middle border-right"
width="260px"
@mousemove="handleMousemove"
@mousedown="handleMousedown"
@mouseup="handleMouseup"
>
<slot name="right"></slot>
</el-aside>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref, watch } from 'vue';
export default defineComponent({
name: 'DragChangeLeftWidth',
props: {
// 是否隐藏左边的元素
hiddenLeft: {
type: Boolean,
default: false,
},
// 左侧元素可拖拽的最小宽度
minWidth: {
type: Number || String,
default: 200,
},
// 左侧元素可拖拽的最大宽度
maxWidth: {
type: Number || String,
default: 900,
},
},
setup(props, { emit }) {
const state = reactive({
// 第一把锁控制鼠标形状改变
resizableLock1: false,
// 第二把锁控制是否可以拖拽
resizableLock2: false,
// 拖拽之前起始x坐标
initialX: 0,
// 拖拽之前起始宽度
initialWidth: 0,
});
// 左侧domRef
const leftAsideRef = ref();
// 右侧aside domRef
const rightAsideRef = ref();
/**
* @description: 左侧指标模块鼠标进入事件
* @return {*}
*/
const handleMousemove = (e: any) => {
// 现获取左侧dom 的左侧x轴坐标
const rightXAxis = leftAsideRef.value?.$el.getBoundingClientRect()?.right;
const leftXAxis = leftAsideRef.value?.$el.getBoundingClientRect()?.left;
// 获取当前鼠标所在x轴的坐标
const mouseXAxis = e.clientX;
// 当鼠标从dom右侧进入5px距离之间时,修改鼠标样式
if (
(rightXAxis >= mouseXAxis && rightXAxis - mouseXAxis <= 10)
|| (rightXAxis < mouseXAxis && mouseXAxis - rightXAxis <= 10)
) {
if (!state.resizableLock2) {
// 只能先开启第一把锁
state.resizableLock1 = true;
}
} else if (!state.resizableLock2) {
// 将鼠标形状复原
// 第二把锁不关第一把锁不能关
state.resizableLock1 = false;
}
// 第一把锁控制鼠标变形
if (state.resizableLock1) {
leftAsideRef.value.$el.style.cursor = 'w-resize';
rightAsideRef.value.$el.style.cursor = 'w-resize';
// 打开第一把锁之后,要将fixed-container子元素设置pointer-event:none
const leftChildList = leftAsideRef.value.$el.children;
leftChildList.forEach((item: any) => {
// eslint-disable-next-line no-param-reassign
item.style.pointerEvents = 'none';
});
const rightChildList = rightAsideRef.value.$el.children;
rightChildList.forEach((item: any) => {
// eslint-disable-next-line no-param-reassign
item.style.pointerEvents = 'none';
});
} else {
leftAsideRef.value.$el.style.cursor = 'default';
rightAsideRef.value.$el.style.cursor = 'default';
// 第一把锁关闭之后,要将fixed-container子元素设置pointer-event:auto
const leftChildList = leftAsideRef.value.$el.children;
leftChildList.forEach((item: any) => {
// eslint-disable-next-line no-param-reassign
item.style.pointerEvents = 'auto';
});
const rightChildList = rightAsideRef.value.$el.children;
rightChildList.forEach((item: any) => {
// eslint-disable-next-line no-param-reassign
item.style.pointerEvents = 'auto';
});
}
if (state.resizableLock2) {
// 只有当可以进行拖拽的时候,才去清除鼠标默认事件
e.preventDefault();
// 第二把锁控制移动
const updateWidth = e.clientX - state.initialX;
// 超出最小值范围则不进行修改宽度操作,但是要更新初始宽度和初始x坐标
if (rightXAxis - leftXAxis <= props.minWidth && updateWidth < 0) {
state.initialX = e.clientX;
state.initialWidth = leftAsideRef.value.$el.offsetWidth;
return;
}
if (rightXAxis - leftXAxis >= props.maxWidth && updateWidth > 0) {
state.initialX = e.clientX;
state.initialWidth = leftAsideRef.value.$el.offsetWidth;
return;
}
const newWidth = state.initialWidth + updateWidth;
leftAsideRef.value.$el.style.width = `${newWidth}px`;
}
};
/**
* @description: 监听鼠标按下事件
* @return {*}
*/
const handleMousedown = (e: any) => {
if (state.resizableLock1) {
// 只有当可以进行拖拽的时候,才去清除鼠标默认事件
e.preventDefault();
// 只有当第一把锁打开的时候,才能打开第二把锁
state.resizableLock2 = true;
}
state.initialX = e.clientX;
state.initialWidth = leftAsideRef.value.$el.offsetWidth;
};
/**
* @description: 鼠标回弹触发事件
* @return {*}
*/
const handleMouseup = () => {
state.resizableLock2 = false;
state.resizableLock1 = false;
};
return {
...toRefs(state),
leftAsideRef,
rightAsideRef,
handleTransitionend,
handleMousemove,
handleMousedown,
handleMouseup,
};
},
});
</script>