SplitPanel-窗口拆分工具库
项目中可能会需要用到窗口分割布局。
比如做一个在线的代码编辑器,左侧是目录,右侧是编码区,下边是控制台。
比如做一个文档浏览页,左侧是目录,有测试内容展示。
需要能手动调整各个区域的占比。
有好多很好很强大的库,比如:
等
如果不想引入特别大的库,可以自己写一个。
先看一下效果
代码如下(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参数指定的宽高,也可以修改一下代码,初始化的时候,获取宽高的方式扩展一下。
这个工具还不太好用,需要配合样式调整。代码也有一些问题。对于简单的场景,自己修改修改,还是很方便的。