需求
实现一个左右布局,其中左侧边栏可以通过拖拽中间的分割线改变宽度。
原理
在位置确定方面
我们拖动的左右交接的那根线(图中浅蓝色那个竖线),并非左右块的边界,而是一个比较窄的div
,我这里设置宽度是4px
;
如果,我们假设左侧边栏的宽度是X,那么有以下推论:
- 在
absolute
布局下,左侧边栏的位置就可以设置为top:0,left:0,bottom:0,width:X
; - 右侧内容部分的位置就可以设为
top:0,bottom:0,right:0,left:X
; - 拖拽条的位置是
top:0,bottom:0,left:X-2,width:4px
所以,我们只需要一个React的状态变量X
就可以确定所有的内容位置和大小。
在拖拽动作方面
- 我们在拖拽条上点击鼠标左键
- 然后进入拖拽状态(整个内容区域被mask遮盖)
- 在拖拽状态下(鼠标左键没有松开),我们左右移动鼠标,通过
e.pageX
获取鼠标的位置,从而确定X
的值。 - 当鼠标左键抬起,退出拖拽状态,mask消失,拖拽过程结束。
这个过程涉及三个动作:
- 拖拽条上的鼠标左键点击
- mask上的鼠标拖拽
- mask上的鼠标左键抬起
到此为止,我们的原理已经非常明白了,我们需要定义两个状态变量,一个是确定左侧宽度的X,另一个是表示进入拖拽状态的Flag。
代码
以下是完整代码:
index.js
* { padding: 0; margin: 0; box-sizing: border-box; } .resizeable-layout { width: 100%; height: 100vh; } .left { position: absolute; left: 0; top: 0; bottom: 0; background-color: antiquewhite; } .right { position: absolute; top: 0; bottom: 0; right: 0; background-color: beige; } .slidebar { position: absolute; top: 0; bottom: 0; width: 4px; cursor: col-resize;//鼠标箭头提示可拖拽 } .dragging-mask { position: absolute; left: 0; top: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.1);//拖拽过程中页面微微变灰 cursor: col-resize; }
复制
App.js
import { useState } from 'react' import './index.css' export default function ResizeableLayout() { const [leftWidth, setLeftWidth] = useState(parseInt(localStorage.getItem('leftWidth')) || 150) const [dragging, setDragging] = useState(false) const slidebarColorOnDragging = 'lightblue' const slidebarColorDefault = 'rgba(0,0,0,0)' const [slidebarColor, setSlidebarColor] = useState(slidebarColorDefault) function handleLeftKeyDown(e) { if (e.button === 0) {//判断是鼠标左键按下 setDragging(true) setSlidebarColor(slidebarColorOnDragging) } } function handleMouseMove(e) { if (dragging) { setLeftWidth(e.pageX) } } function handleMouseUp(e) { setDragging(false) setSlidebarColor(slidebarColorDefault) localStorage.setItem('leftWidth', leftWidth) } return ( <div className="resizeable-layout"> <div className="left" style={{ width: leftWidth }}> left </div> <div className="right" style={{ left: leftWidth }}> right </div> <div className='slidebar' style={{ left: leftWidth - 2, background: slidebarColor }} onMouseDown={(e) => handleLeftKeyDown(e)}></div> {dragging && <div className='dragging-mask' onMouseMove={(e) => handleMouseMove(e)} onMouseUp={(e) => handleMouseUp(e)} ></div>} </div> ) }
复制