20
2024
09

SplitPanel-窗口拆分工具库

项目中可能会需要用到窗口分割布局。

比如做一个在线的代码编辑器,左侧是目录,右侧是编码区,下边是控制台。

比如做一个文档浏览页,左侧是目录,有测试内容展示。

需要能手动调整各个区域的占比

有好多很好很强大的库,比如:

splitpanes

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参数指定的宽高,也可以修改一下代码,初始化的时候,获取宽高的方式扩展一下。

这个工具还不太好用,需要配合样式调整。代码也有一些问题。对于简单的场景,自己修改修改,还是很方便的。


源码打包下载

« 上一篇下一篇 »

相关文章:

js拖拽工具库  (2024-9-20 9:34:48)

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。