拖拽功能由拖拽条、面板组成,如果为每个拖拽条单独添加鼠标事件,每个面板单独修改宽度,会增加很多行代码和工作量。因此考虑封装的方式较少代码量,首先是拖拽条的封装
1class DragElement {
2 static paramEleHistory = new Set();
3
4 constructor(dragEle, customDragMove) {
5 this.dragEle = dragEle;
6 this.isEnabled = true;
7 this.customDragMove = customDragMove;
8 this.dragEleBeginPos = undefined;
9 if (DragElement.paramEleHistory.has(this.dragEle)) {
10 return [...DragElement.paramEleHistory].find(item => item === this.dragEle);
11 }
12 DragElement.paramEleHistory.add(this.dragEle);
13
14 this.isBeginMove = false;
15 this.mouseBeginPos = undefined;
16 this.draggable();
17 }
18 // 鼠标位置初始值
19 onMoveDown(x, y) {
20 // todo 等待外部传入
21 }
22 onMouseMove(offSetX, offsetX, x, y) {
23 // todo 等待外部传入
24 }
25 // 鼠标位置结束
26 onMouseUp(x, y) {
27 // todo 等待外部传入
28
29 }
30 onMouseLeave(x, y) {
31 // todo 等待外部传入
32
33 }
34
35 // 一系列工具函数
36
37
38 draggable = () => {
39 this.dragEle.addEventListener('touchstart', (event) => {
40 const touch = event.touches[0];
41 this.onMouseDownHandler(touch.clientX, touch.clientY);
42 });
43 this.dragEle.addEventListener('mousedown', (event) => {
44 this.onMouseDownHandler(event.x, event.y);
45 });
46
47 document.addEventListener('mousemove', (event) => {
48 this.dragEle.classList.add("ew-resize");
49 event.preventDefault();
50 if (this.isBeginMove) {
51 this.setDragElePos(event.x, event.y);
52 }
53 });
54 document.addEventListener('touchmove', (event) => {
55 event.preventDefault();
56 const touch = event.touches[0];
57 if (this.isBeginMove) {
58 this.setDragElePos(touch.clientX, touch.clientY);
59 }
60 }, { passive: false });
61
62 document.addEventListener('mouseup', (event) => {
63 this.isBeginMove = false;
64 document.body.classList.remove("ew-resize");
65
66 this.onMouseUp && this.onMouseUp(event.x, event.y);
67
68 });
69
70 this.dragEle.addEventListener('mouseleave', (event) => {
71 this.onMouseLeave();
72
73 });
74 this.dragEle.addEventListener('touchend', (event) => {
75 this.isBeginMove = false;
76 const touch = event.touches[0];
77 document.body.classList.remove("ew-resize");
78 this.onMouseUp && this.onMouseUp(touch.clientX, touch.clientY);
79 });
80
81 }
82}
1class PanelResize {
2 constructor(parentEle) {
3 // 一系列状态
4 this.init()
5 }
6init() {
7// 获取面板及拖拽元素
8const children = Array.from(this.parentEle.children);
9 this.totalWidth = this.getTotalWidth();
10
11 children.forEach(child => {
12 if (child.id.includes('panel')) {
13 this.panelsEle.push(child);
14 } else if (child.id.includes('handle')) {
15 this.handlesEle.push(child);
16 }
17 });
18}
19}
1<body>
2 <div class="wrapper" id="wrapper-resize">
3 <div class="panel-one">panel-one</div>
4 <div class="handle-one"></div>
5 <div class="panel-two">panel-two</div>
6 <div class="handle-two"></div>
7 <div class="panel-three">panel-three</div>
8 </div>
9 <script>
10 // 传入父元素
11 const panelResize = new PanelResize(document.getElementById('wrapper-resize'), [[10,30], [20,30], [20,40]], 'vertical');
12
13 </script>
级联面板是在拖拽时,首先对拖拽条左右面板进行伸缩,如果不够了,计算剩余量,将剩余量应用到下一个面板。以次类推,直到所有面板应用完毕,或者剩余量为0。但这种思路有一个问题是,如果面板直接应用偏移量,那剩余面板可能不能符合最大/最小限制,这样就不能保证每个面板都要在限制的范围内进行拖拽。所以就需要先知道总体偏移量,再结合限制范围进行偏移
1// 计算左右偏移量
2 const posMoveNumber = this.isLeftMove(offset);
3 const negMoveNumber = this.isRightMove(offset);
4 // 不能再偏移,直接返回
5 if (posMoveNumber <= Math.abs(offset) || negMoveNumber <= Math.abs(offset)) {
6 return;
7 }
8 // 比较左右和鼠标偏移量并进行偏移
9 const minMoveNumber = offset > 0
10 ? Math.min(offset, posMoveNumber, negMoveNumber)
11 : Math.max(offset, -posMoveNumber, -negMoveNumber);
12 moveLeftPanel(minMoveNumber);
13 moveRightPanel(minMoveNumber);
级联面板是实现多面板的难点,可以通过预先计算偏移量实现。还有一些小细节要注意,比如因为是动态布局,一个面板的宽度变化会影响其他面板,所以需要预先保存各个面板的原始值,在计算好各自的偏移量后,一起修改。