项目中可能会需要用到窗口分割布局。
比如做一个在线的代码编辑器,左侧是目录,右侧是编码区,下边是控制台。
比如做一个文档浏览页,左侧是目录,有测试内容展示。
需要能手动调整各个区域的占比。
有好多很好很强大的库,比如:
等
如果不想引入特别大的库,可以自己写一个。
先看一下效果
代码如下(splitPaneUtil.js):
依赖自己写的另外一个库 dragUtil.js
function PaneUtil(ele) { this.ele = ele; this.arr = []; var eles = this.ele.querySelectorAll('.h-split-h,.h-split-v'); for(var i = 0; i < eles.length; i++) { this.arr.push(new Split(eles[i])); } } PaneUtil.prototype.destroy = function() { this.ele = null; for(var i = 0; i < this.arr.length; i++) { this.arr[i].destroy(); } this.arr = []; } function SplitPaneUtil() { this.arr = []; this.onDown = function(ele, id, x, y, e){}; this.onMove = function(ele, id, x, y, dx, dy, e){} this.onUp = function(ele, id, x, y, e){}; } SplitPaneUtil.prototype.add = function(ele) { this.remove(ele); this.arr.push(new PaneUtil(ele)); } SplitPaneUtil.prototype.remove = function(ele) { var ind = this.get(ele); if(ind != -1) { this.arr[ind].destroy(); this.arr.splice(ind, 1); } } SplitPaneUtil.prototype.get = function(ele) { for(var i = 0; i < this.arr.length; i++) { if(this.arr[i].ele == ele) { return i; } } return -1; } window.SplitPaneUtil = new SplitPaneUtil(); function Split(ele) { this.ele = ele; this.vertical = ele.classList.contains('h-split-v'); this.dragUtil = new DragUtil(this.ele, this.down.bind(this), this.move.bind(this), this.up.bind(this), true, true, true); } Split.prototype.destroy = function() { // console.log("split destroy") this.ele = null; this.dragUtil.destroy(); } Split.prototype.down = function(id, x, y, e) { window.SplitPaneUtil.onDown(this.ele, id, x, y, e); } Split.prototype.up = function(id, x, y, e) { window.SplitPaneUtil.onUp(this.ele, id, x, y, e); } Split.prototype.move = function(id, x, y, dx, dy, e) { var ele = this.ele; var parent = this.ele.parentElement; var children = parent.children; var ind = this.find(children, ele); var prev = children[ind-1]; var next = children[ind+1]; var pSize = this.getParentSize(parent); this.moveEle(prev, dx, dy); this.moveEle(next, -dx, -dy); var size = this.getTotalSize(children); // console.log(size, pSize); if(size > pSize) { var dirX = dx > 0 ? 1 : -1; var dirY = dy > 0 ? 1 : -1; var ds = size - pSize; // console.log(ds, dirX, dirY); this.moveEle(prev, -dirX * ds, -dirY * ds); this.moveEle(next, dirX * ds, dirY * ds); } window.SplitPaneUtil.onMove(this.ele, id, x, y, dx, dy, e); } Split.prototype.getParentSize = function(parent) { return this.vertical ? parent.getBoundingClientRect().height : parent.getBoundingClientRect().width; } Split.prototype.getTotalSize = function(children) { var size = 0; for(var i = 0; i < children.length; i++) { var ele = children[i]; if(ele.style.width) { size += parseFloat(ele.style.width); } if(ele.style.height) { size += parseFloat(ele.style.height); } } return size; } Split.prototype.moveEle = function(ele, dx, dy) { if(ele.style.width) { ele.style.width = parseFloat(ele.style.width) + dx + "px"; } if(ele.style.height) { ele.style.height = parseFloat(ele.style.height) + dy + "px"; } this.onEleResize(ele); } Split.prototype.onEleResize = function(ele) { if(ele.onResize) { ele.onResize(); } for(var i = 0; i < ele.children.length; i++) { this.onEleResize(ele.children[i]); } } Split.prototype.find = function(arr, ele) { for(var i = 0; i < arr.length; i++) { if(arr[i] == ele) { return i; } } return -1; }
测试代码(index.html):
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>窗口分割工具</title> <style type="text/css"> .panel { width: 600px; height: 600px; border: solid 1px #000; display: flex; flex-direction: row; user-select: none; -webkit-user-drag: none; } .left { } .right { flex-grow: 1; display: flex; flex-direction: column; } .top { } .middle { flex-grow: 1; overflow: hidden; min-height: 0; } .bottom { } .h-split-v { cursor: ns-resize; height: 10px; -webkit-user-drag: none; background-color: #ccc; } .h-split-h { cursor: ew-resize; width: 10px; -webkit-user-drag: none; background-color: #ccc; } </style> <script type="text/javascript" src="./dragUtil.js"></script> <script type="text/javascript" src="./splitPaneUtil.js"></script> <script type="text/javascript"> function onLoad() { SplitPaneUtil.add(document.body); } </script> </head> <body onload="onLoad()"> <div class="panel"> <div class="left" style="width:150px;">left</div> <div class="h-split-h" style="width: 10px;"></div> <div class="right"> <div class="top" style="height:100px;">top</div> <div class="h-split-v" style="height: 10px;"></div> <div class="middle">middle</div> <div class="h-split-v" style="height: 10px;"></div> <div class="bottom" style="height:100px;">bottom</div> </div> </div> </body> </html>
使用的时候,只需要定义好dom结构,调用SplitPaneUtil.add,会自动遍历指定dom元素下所有的包含分割线的元素。
不需要的时候,调用remove即可。
分割线class是固定的,代码中会自动获取。
需要通过style指定宽或高,只有一个可以是自动计算的,其余都需要指定。
只认通过style参数指定的宽高,也可以修改一下代码,初始化的时候,获取宽高的方式扩展一下。
这个工具还不太好用,需要配合样式调整。代码也有一些问题。对于简单的场景,自己修改修改,还是很方便的。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。