「AntV」如何用AntV X6实现思维导图节点收缩功能?
上一篇文章写的是怎么用X6做一个类似于processOn的思维导图,但是是没有做节点的收缩展开功能的,这种功能应该如何实现呢?
处理收缩节点按钮生成
上一篇文章中,在注册html节点时,有一个creatHtmlDom的方法,内部存在一个创建子节点的createChildNode的方法(当前只放了当前功能相关的代码)
export const createChildNode = (data, propsData) => {
// 创建子节点部分
...
...
// 核心部分,创建收缩节点时那个圈圈节点
if (data.children && data.children.length) {
const collapseNode = document.createElement("span");
const isCollapsed = !!data.collapsed;
let originName = "";
if (isCollapsed) {
originName = "mindmap__wrap-collapsed";
if (data.count > 99) {
const i = document.createElement("i");
i.classList.add("xq-icon-more");
collapseNode.appendChild(i);
} else {
collapseNode.innerText = data.count;
}
} else {
originName = "mindmap__wrap-open";
}
let collapseName = "";
collapseNode.className = originName;
if (propsData.direction == "LR") {
collapseName = originName + "-right";
} else if (propsData.direction == "RL") {
collapseName = originName + "-left";
} else if (propsData.direction == "TB") {
collapseName = originName + "-bottom";
} else {
collapseName = originName + "-top";
}
// 这个是处理节点居中还是不居中的模式,不用考虑
if (!propsData.heightCenter) {
collapseNode.classList.add("not-center");
}
collapseNode.classList.add(collapseName);
// 绑定收缩展开事件
collapseNode.setAttribute("event", "node:collapse");
collapseNode.style.borderColor = data.textLine || propsData.textLine;
collapseNode.style.background = propsData.backgroundColor;
collapseNode.style.color = data.textLine || propsData.textLine;
textNode.appendChild(collapseNode);
}
// wrap:子节点最顶级
// textNode: 文字节点
wrap.appendChild(textNode);
}
// 样式核心部分
// 通过opacity来控制节点的显示与隐藏,如果通过visible之类的控制,会有鼠标悬停到节点没有触发节点展示的bug
.mindmap__wrap-open {
opacity: 0;
}
.mindmap__wrap-collapsed {
display: inline-block;
width: 7px;
height: 7px;
border-width: 1px;
border-style: solid;
border-radius: 7px;
position: absolute;
// visibility: hidden;
cursor: pointer;
&:hover {
opacity: 1;
}
}
// 其他部分就是处理对圈圈节点的定位了(注意组件上下左右四个方向的布局)
处理收缩展开事件
this.graph.on("node:collapse", ({ node, e }) => {
const p = node.getPosition();
const _nodeData = node.data.nodeData;
const children = _nodeData.children;
// 获取当前节点收缩效果,true代表当前节点的子节点都被收缩,false则相反
const hasCollapsed = _nodeData.collapsed;
// 计算当前节点下子节点的全部数量
const count = collapsedNodes(children);
// 找到目标数据并设置切换数据效果
// 注意:这个字段如何有作用?往下看
let { node: node2 } = findItem(this.mindData, _nodeData.id);
// 给数据设置hasCollapsed
Vue.set(node2, "collapsed", !hasCollapsed);
// 这个count做节点收缩时,展示的数据
node2.count = count;
// 重置编辑节点
// 目的是,当前操作`node:collapse`事件时,如果当前画布中存在编辑类型的输入框节点,
// 需要将输入框转成正常节点
this.resetEditNode();
if (
!hasCollapsed &&
this.originChoosedNode &&
this.originChoosedNode.dataset.id !== _nodeData.id
) {
this.currentData = {};
}
e.stopPropagation();
// 注意!!!非常重要
// 当收缩的当前节点子节点数量过大时,会造成画布偏移,
// 你点击时,圈圈的位置跟你收缩后,节点位置不一致(看下面两张图),此时需要计算画布偏移量进行重新归位
setTimeout(() => {
const _node = this.graph.getCellById(node.id);
const p2 = _node.getPosition();
const pTranslate = this.graph.translate();
// 注意!一定要乘以缩放比例,不然纵向布局无法定位到刷新之前的位置
const zoom = this.graph.zoom();
const deviationX = pTranslate.tx - (p2.x - p.x) * zoom;
const deviationY = pTranslate.ty - (p2.y - p.y) * zoom;
this.graph.translate(deviationX, deviationY);
});
});
如何让收缩事件生效?
getChildren: (d) => {
// 此部分是收缩节点时使用
const hasCollapsed = !!d.collapsed;
return hasCollapsed ? null : d.children;
},
好了,让我们看看效果: 我录了视频但是我不会放,谁会的可以教教我。。。
最后,感谢大家阅读我的小文章,请帮我点亮一下我的小心心吧!!!



转载自:https://juejin.cn/post/7283798844235808803