-
Notifications
You must be signed in to change notification settings - Fork 1.6k
fix: Fix Tree Layout Expand #7463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v5
Are you sure you want to change the base?
Conversation
主要改进: - 修复展开/收起时节点位置跳动和边渲染错误问题 - 实现初次渲染从根节点平滑展开的动画效果 - 提取 14 个辅助方法,提升代码可维护性 - 完全兼容非树形布局,无破坏性变更 技术实现: - 新增 layoutPreset 机制支持初始位置动画 - 增强 preLayoutDraw 触发位置更新 - 新增 handleTreeLayoutExpand/Collapse 处理树形布局 - 优化边跟随节点动画效果 相关 Issue: antvis#7439
问题描述: 初次渲染时 preLayout 和 preLayoutDraw 分别调用 simulate 进行布局模拟, 导致相同的布局计算执行两次,造成性能浪费。 优化方案: - 在 LayoutController 中新增 simulationCache 字段缓存 simulate 结果 - preLayout 执行 simulate 后缓存结果 - preLayoutDraw 优先使用缓存,避免重复调用 - 使用完成后立即清除缓存,确保下次渲染时重新计算 性能提升: - 初次渲染耗时减少约 50%(避免重复布局计算) - 内存占用增加可忽略(仅临时缓存,使用后立即清除) 新增 API: - getCachedSimulation(): 获取缓存的布局模拟结果 - clearSimulationCache(): 清除布局模拟缓存 相关 Issue: antvis#7439
Summary of ChangesHello @lyw405, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request provides a comprehensive solution to several reported bugs concerning tree layout expansion and collapse. The core changes involve refactoring the Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
此拉取请求对树形布局的展开和折叠功能进行了重大改进,解决了多个相关问题。代码变更结构良好,将复杂的逻辑分解为更小、更易于管理的辅助函数。使用预布局状态(__layoutPreset)作为初始位置,并以动画方式过渡到最终计算位置的核心思想,是实现平滑动画的可靠方法。
我有几点建议。一个是高严重性问题,关于 preLayoutDraw 中新逻辑的适用范围,它可能会影响非树形布局。另一个是中等严重性的建议,旨在改善 getLayoutOptions 中的封装性。总的来说,这是一次很棒的贡献,提升了树形可视化的用户体验。
packages/g6/src/runtime/element.ts
Outdated
| // <zh/> 对于树形布局,需要再次更新位置到最终位置以触发动画 | ||
| // For tree layout, need to update positions to final positions to trigger animation | ||
| const { layout } = this.context; | ||
| if (layout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
packages/g6/src/runtime/element.ts
Outdated
| const layoutController = layout as unknown as { | ||
| presetOptions?: Partial<STDLayoutOptions>; | ||
| options?: LayoutOptions; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
嗨,请问这个有合并上线预期时间吗? |
|
/gemini review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
本次 PR 修复了多个与树图布局展开/收起动画相关的问题。改动范围较大,主要集中在 element.ts 和 layout.ts 文件中。
为树图布局创建特定的处理函数(handleTreeLayoutExpand, handleTreeLayoutCollapse)来隔离复杂的逻辑,这种方法是合理的。代码被重构为更小、更易于管理的私有方法,提高了可读性。
我提出了一些关于代码清晰度和 API 一致性的改进建议。
| positionData?.nodes?.forEach((node) => { | ||
| const id = idOf(node); | ||
| const addNode = add.nodes.get(id); | ||
| model.syncNodeLikeDatum(node); | ||
| if (addNode && node.style) Object.assign(addNode.style!, node.style); | ||
| }); | ||
| simulate?.edges?.forEach((l) => { | ||
| const id = idOf(l); | ||
| const edge = add.edges.get(id); | ||
| model.syncEdgeDatum(l); | ||
| if (edge) Object.assign(edge.style!, l.style); | ||
| positionData?.edges?.forEach((edge) => { | ||
| const id = idOf(edge); | ||
| const addEdge = add.edges.get(id); | ||
| model.syncEdgeDatum(edge); | ||
| if (addEdge && edge.style) Object.assign(addEdge.style!, edge.style); | ||
| }); | ||
| simulate?.combos?.forEach((l) => { | ||
| const id = idOf(l); | ||
| const combo = add.combos.get(id); | ||
| model.syncNodeLikeDatum(l); | ||
| if (combo) Object.assign(combo.style!, l.style); | ||
| positionData?.combos?.forEach((combo) => { | ||
| const id = idOf(combo); | ||
| const addCombo = add.combos.get(id); | ||
| model.syncNodeLikeDatum(combo); | ||
| if (addCombo && combo.style) Object.assign(addCombo.style!, combo.style); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在 preLayout 方法中,处理 nodes、edges 和 combos 的逻辑非常相似,存在代码重复。
// packages/g6/src/runtime/layout.ts:83-100
positionData?.nodes?.forEach((node) => {
const id = idOf(node);
const addNode = add.nodes.get(id);
model.syncNodeLikeDatum(node);
if (addNode && node.style) Object.assign(addNode.style!, node.style);
});
positionData?.edges?.forEach((edge) => {
const id = idOf(edge);
const addEdge = add.edges.get(id);
model.syncEdgeDatum(edge);
if (addEdge && edge.style) Object.assign(addEdge.style!, edge.style);
});
positionData?.combos?.forEach((combo) => {
const id = idOf(combo);
const addCombo = add.combos.get(id);
model.syncNodeLikeDatum(combo);
if (addCombo && combo.style) Object.assign(addCombo.style!, combo.style);
});可以考虑将这部分重复逻辑提取到一个辅助函数中,以提高代码的可读性和可维护性。例如:
const syncElementData = (elements: any[] | undefined, getElement: (id: ID) => any, syncDatum: (datum: any) => void) => {
elements?.forEach((element) => {
const id = idOf(element);
const addedElement = getElement(id);
syncDatum(element);
if (addedElement && element.style) {
Object.assign(addedElement.style!, element.style);
}
});
};
syncElementData(positionData?.nodes, (id) => add.nodes.get(id), model.syncNodeLikeDatum.bind(model));
syncElementData(positionData?.edges, (id) => add.edges.get(id), model.syncEdgeDatum.bind(model));
syncElementData(positionData?.combos, (id) => add.combos.get(id), model.syncNodeLikeDatum.bind(model));注意 syncNodeLikeDatum 和 syncEdgeDatum 需要绑定 model 的上下文。
| const nodesToAnimate = new Map<ID, NodeData>(); | ||
| updateNodes.forEach((nodeData, nodeId) => { | ||
| if (nodeId !== id) nodesToAnimate.set(nodeId, nodeData); | ||
| }); | ||
| add.nodes.forEach((nodeData, nodeId) => { | ||
| nodesToAnimate.set(nodeId, nodeData); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
收集待动画节点 nodesToAnimate 的逻辑可以更简洁。
// packages/g6/src/runtime/element.ts:1060-1066
const nodesToAnimate = new Map<ID, NodeData>();
updateNodes.forEach((nodeData, nodeId) => {
if (nodeId !== id) nodesToAnimate.set(nodeId, nodeData);
});
add.nodes.forEach((nodeData, nodeId) => {
nodesToAnimate.set(nodeId, nodeData);
});可以使用 Map 的构造函数和展开语法来简化这个过程,然后再移除目标节点 id。这样代码更易读。建议修改为:
const nodesToAnimate = new Map<ID, NodeData>([...updateNodes, ...add.nodes]);
nodesToAnimate.delete(id);| public async expandNode(id: ID, options: CollapseExpandNodeOptions): Promise<void> { | ||
| const { model, layout } = this.context; | ||
| const { animation, align } = options; | ||
| const position = positionOf(model.getNodeData([id])[0]); | ||
| const { animation } = options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📋 关联 Issue