【javascript】纯原⽣js的轻便组织结构图,树状图,⽀持⾃定
义样式
前⾔
写这个插件呢,只是⼀时兴起,公司的项⽬中使⽤到了组织结构图,然后本着⾯向百度、⾕歌编程的思想,我去摸索了半天。
结果⼀⽆所获,可能这个词⽤的有点重了,但是就是没有符合我公司项⽬要求的,然后我⼜去了全球最⼤的同性社交⽹站(github)还是没有到⾃⼰满意的插件。
但是整个过程借鉴下来也有了⾃⼰的⼀些⼩思路,⼲脆⾃⼰封装⼀个得了(本⽂的插件⾮常简单,⼤佬勿喷)
效果图
没图没真相,先放效果图
1.竖向渲染效果图
2.横向渲染效果图
在此就不放 gif 图了,节点皆可动态显⽰和隐藏,也⽀持⾃定义样式。
思路和实现
思路呢,⼤概是这样的,先定数据结构,因为有了结构才好拿着去定 dom 的结构。
因为是结构图嘛,所以⽗⼦关系肯定是跑不掉了,那基本的关系就如下了:
{
body: {}, //这个代表当前节点
children: [], //这个代表⼦节点
}
有⼈问那如果还有孙⼦节点怎么办?诶,问的好!那就开始套娃了(刚好⽅便后期递归渲染),所以结构如下:
{
//这个代表当前节点
body: {},
//这个代表⼦节点
children: [
{
//这个代表孙⼦节点
body: {},
...
}
],
}
ok,结构搞定以后,就去拿着⽣成 dom 吧,这⾥我简单写个了⽅法⽤来创建元素,省的每次都写⼀堆...
// 创建元素的⽅法
function createEl(tag, domStyle, domClass) {
let _dom = ateElement(tag);
Object.assign(_dom.style, domStyle);
if (domClass) _dom.className = domClass;
return _dom;
}
是不是简单明了,传标签名、标签的内联样式、标签的类名就可以直接⽣成 dom 元素了。
搞定了创建元素的⽅法,在此基础上再去写⼀个创建节点的⽅法,把 body ⾥⾯的新增两个字段
2.className 代表当前节点的类名,⽀持多个类名,⽤空格隔开就⾏。
有了这两个字段,基本上节点就具备基础的内容和类名了,创建节点⽅法如下:
/
/ 创建节点⽅法
function createNode(body){
let _body = body;
let _node = createEl("div", {
margin: "20px",
padding: "10px",
border: "1px solid #2288ff"
boxSizing: "border-box",
position: "relative",
flexShrink: 0
}, _body.className);
_node.innerHTML = _t;
return _node;
}
原生js和js的区别ok,现在创建元素和创建节点的⽅法都搞定了,下⾯就是要根据结构去渲染出来节点了,这⾥为了更直观的渲染出来结构,我使⽤了 flex 布局。
为了省事,这⾥直接贴递归渲染节点⽅法的思路,如下:
// 递归创建节点的⽅法
// data 是数据源、wrap是挂载的容器、id是后⾯⽤来表名⽗⼦关系的标识
let nodeNum = 0;
function renderNode(data,wrap,id){
// 1.创建⽗元素节点
let parentNode = createEl("div", { display: "flex", alignItems: "center", userSelect: "none" });
// 2.创建当前元素节点
let currentNode = createNode(data.body);
// 注意这⾥每个节点上⾯都有属性 id 和 fid 就是为了后⾯表名⽗⼦关系的标识
currentNode.setAttribute("data-id", ++nodeNum);
currentNode.setAttribute("data-fid", id ? id : 0);
// 添加公共类名,这⾥防⽌覆盖原有的类名,所以使⽤ classList.add ⽅法
currentNode.classList.add("tree-map-node");
// 3.是否给当前元素节点创建伸缩符
if (!!data.shrink && !!data.children && data.children.length > 0){
// 符合这个条件就渲染,然后⽗相⼦绝放到当前元素上⾯(记得绑定点击事件)
......
}
// 4.创建⼦元素节点
if (!!data.children && data.children.length > 0 && !data.hidden) {
// 有⼦节点就⾛这个⽅法
let childrenNode = createEl("div");
parentNode.appendChild(currentNode);
parentNode.appendChild(childrenNode);
// 注意:因为是使⽤的递归(深度遍历优先)所以parentId需要根据DOM结构去取出来!
let parentId = Attribute("data-id");
for (let index = 0; index < data.children.length; index++) {
renderNode(data.children[index], childrenNode, parentId);
}
} else {
// 没有就直接不创建⼦节点
parentNode.appendChild(currentNode);
}
// 5.最后挂载到外部容器中
wrap.appendChild(parentNode);
}
ok,到此节点就全部渲染出来了,渲染出来后,就根据之前说的 id 和 fid 去绘制两点之间的连线,⽅法如下:
// 绘制节点之间的连线
function drawline() {
// 此处写你给每个节点起的公共类名
let selector = ".tree-map-node";
// 获取所有渲染出来的节点
let treeNodes = document.querySelectorAll(selector);
treeNodes.forEach(function (currentNode) {
// 根据 fid 去选中他的⽗元素节点
let parentNode = document.querySelector(".tree-map-node[data-id='" + Attribute("data-fid") + "']");
if (parentNode) drawLineBetweenNode(parentNode, currentNode);
})
}
// 连接节点的⽅法
function drawLineBetweenNode(parentNode,childNode){
// 这⾥根据offsetWidth、offsetHeight、offsetLeft、offsetTop
// 到两点之间的中⼼点和关系就可以连线了
// 具体代码就省略了,可以看源码
......
}
到此,插件主要的思路和实现⽅法都介绍完了,剩下的就是把他们组合起来,并且⽀持⾃定义样式了,具体的就不在此展开了。插件已经上传 和 可以点击传送门查看。
总结
1. ⽬前只定义了⽂本的节点,如果想新增可以在 calssName 上新增类名,根据类名⾃定义样式
2. 渲染⽅向⽬前只⽀持两种,横向(horizontal)和竖向(vertical)
3. 在 vue 中使⽤的时候建议⽤ npm 安装⽅式,并且在 mounted ⽣命周期中实例化
4. 其他⽅式直接引⼊对应的 js ⽂件,然后直接调⽤实例化⽅法就可以
5. ⽬前尚存在⼀些问题,空闲时间会想办法解决,浏览器兼容性未测试,主流皆可以
6. 写这个只是⾃⼰的项⽬⽤到了,然后⾃⼰就想着封装了⼀个,欢迎⼤家提建议完善
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论