element-ui 提供了一个 nav-menu 组件,默认情况下该组件没有拖动调整大小的功能。下面通过代码为 nav-menu 添加这个功能。
鼠标悬于 nav-menu 上时,鼠标指针默认为 pointer,为了更好的指示调整能够调整菜单大小的功能,可以设置鼠标样式为 cursor: col-resize。当然显示为列调整大小的鼠标指针不能为一整个菜单,我这里的实现方案时给菜单添加一个不可见的 div,然后鼠标悬于这个 div 上时,才显示col-resize指针。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style>
body {
margin: 0;
position: relative;
height: 100vh;
}
.menu-wrapper {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 300px;
background-color: #ccc;
}
.resize-bar {
position: absolute;
top: 0;
right: 0px;
width: 10px;
height: 100%;
cursor: col-resize;
background-color: orange;
}
.content {
margin-left: 300px;
height: 100%;
background-color: #999;
}
style>
head>
<body>
<div class="menu-wrapper">
<div class="resize-bar">div>
div>
<div class="content">div>
body>
html>
用户按下鼠标并移动鼠标指针,会触发 mousedown、mousemove 事件,松开鼠标会触发 mouseup 事件,于是拖动的时机和顺序是:
mousedown -> 拖动 flag truemousemove -> 检查拖动 flag 是否为 true,如果是执行拖动逻辑mouseup -> 设置拖动 flag 为 false,并且可以取消事件监听等方法给 resize-bar 添加 mousedown 监听,因为只有在这个元素上按下鼠标才表示要调整菜单大小
给 window 添加 mousemove 监听,能够避免移动鼠标时,元素宽度没更上鼠标指针而导致的移动中断
同理给 window 添加 mouseup 监听。
let resizing = false
resizeBarElement.addEventListener('mousedown', () => {
resizing = true
})
window.addEventListener('mousemove', handleResizeMenu) // 见下文
window.addEventListener('mouseup', () => {
resizing = false
})
实际上控制 menu 的大小只需依赖鼠标的偏移值,在 mousemove 的回调中,获取 e.screenX 就能得知当前鼠标的位置,把菜单的大小设置为鼠标的位置即可。
function handleResizeMenu(e) {
if(!resizing) {
return ;
}
const { screenX } = e
// 加上这两行代码,避免移动鼠标时选择界面元素
e.preventDefault()
e.stopPropagation()
menu.style.width = screenX + 'px'
}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style>
body {
margin: 0;
position: relative;
height: 100vh;
}
.menu-wrapper {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 300px;
background-color: #ccc;
}
.resize-bar {
position: absolute;
top: 0;
right: 0px;
width: 10px;
height: 100%;
cursor: col-resize;
background-color: orange;
}
.content {
margin-left: 300px;
height: 100%;
background-color: #999;
}
style>
head>
<body>
<div class="menu-wrapper">
<div class="resize-bar">div>
div>
<div class="content">div>
<script>
let resizing = false
const menu = document.querySelector('.menu-wrapper')
const resizeBar = document.querySelector('.resize-bar')
resizeBar.addEventListener('mousedown', () => {
resizing = true
})
window.addEventListener('mousemove', (e) => {
if (resizing) {
e.preventDefault()
e.stopPropagation()
const { screenX } = e
menu.style.width = screenX + 'px'
}
})
window.addEventListener('mouseup', () => {
resizing = false;
})
script>
body>
html>
这里并没有动态调整 content 的 margin-left,感兴趣的同学可以加上。
上面的代码没有限制鼠标移动的监听频率,会有性能浪费。我们可以在 mousemove 监听中计算鼠标的偏移量,如果偏移量大于指定值才调整大小。具体的实现思路是初始化一个 prevCursorOffset = -1,然后在监听代码中判断:
let prevCursorOffset = -1
window.addEventListener('mousemove', (e) => {
if (resizing) {
e.preventDefault()
e.stopPropagation()
const { screenX } = e
if(prevCursorOffset === -1) {
prevCursorOffset = screenX
// 鼠标偏移量大于十时执行一次大小调整
} else if (Math.abs(prevCursorOffset - screenX) >= 10) {
menu.style.width = screenX + 'px'
prevCursorOffset = screenX
}
}
})