[Bug]: G6辐射状布局下使用image类型节点,多次连续展开节点,控制台稳定报错Uncaught TypeError: Cannot read properties of undefined (reading 'src')
- Dominant language
- TypeScript
- Stars
- 12.3k
- Forks
- 1.6k
- PR merge metrics
- No merged PRs in 30d
Description
### Describe the bug / 问题描述
G6使用image类型节点,多次连续展开节点,控制台稳定报错
```javascript
const graphOptions = useMemo(() => {
const layoutConfig = {
type: 'dendrogram', // 布局类型:dendrogram | tree | radial,默认值dendrogram
radial: true, // 是否按照辐射状布局。若 radial 为 true,建议 direction 设置为 LR 或 RL。
direction: 'RL', // 布局方向:LR | RL | TB | BT | H | V,默认值RL(从右到左,根右子左)
preLayout: true,
};
return {
autoFit: 'center', // 是否自动适应,view | center
autoResize: true, // 是否自动调整画布大小,默认值false
zoom: 1, // 缩放比例,默认值1
data: directoryData,
compact: true,
layout: layoutConfig,
// defaultExpandLevel: props.level,
defaultExpandLevel: 2, // 默认展开层级,若不指定或指定0,将展开全部
containerStyle: {
backgroundImage: `url(${tupuBackImg})`,
backgroundSize: 'cover', // center:完整显示;cover:会被裁减
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center', // 居中
},
node: {
type: 'image',
// type: 'circle',
style: {
src: (model) => {
const depth = model?.depth || 0;
if (model?.isDirectory === false) {
return pointIcon;
}
const depthImgMap = {
0: subjectIcon,
1: level1Icon,
2: level2Icon,
3: level3Icon,
4: level4Icon,
5: pointIcon,
};
return depthImgMap[depth];
},
labelText: (d) => d.directoryName, // 节点文案从directoryName字段取值
labelPlacement: (model) => {
// 根节点文案放在中间,其他节点放在下面
return model?.depth === 0 ? 'center' : 'bottom';
}, // 节点文案位置:'left'|'right'|'top'|'bottom'|'center'|'left-top'|'left-bottom'|'right-top'|'right-bottom'|'top-left'|'top-right'|'bottom-left'|'bottom-right'|[number,number]
labelFill: (model) => {
// 根节点使用黑色,其他节点使用灰色
return model?.depth === 0 ? '#FFFFFF' : '#B2B2B2';
}, // 节点文案颜色
labelFontSize: (model) => {
// 根节点使用16px,其他节点使用10px
return model?.depth === 0 ? 18 : 10;
}, // 节点文案字体大小
labelLineHeight: 1.5, // 节点文案行高
labelFontWeight: 'bold', // 节点文案字体粗细
labelFontFamily: 'SourceHanSansCN, SourceHanSansCN', // 节点文案字体
labelWordWrap: true, // 节点文案是否自动换行
labelMaxWidth: 100, // 节点文案最大宽度
labelBackground: false, // 节点文案是否显示背景
labelBackgroundFill: 'blue', // 节点文案背景颜色
labelPadding: [4, 8], // 节点文案内边距
size: (model) => {
const depth = model.depth || 0;
switch(depth) {
case 0: // 根节点
return 80;
case 1: // 第一层
return 60;
case 2: // 第二层
return 40;
default: // 其他层级,包括考点
return 40;
}
},
halo: true, // 节点光环效果,如果节点是图片,就是图片边框
haloStrokeOpacity: 0.25, // 节点光环边框透明度
},
// 自定义节点状态样式
state: {
// 选中状态样式
selected: { // 配合graph.setElementState(target.id, 'selected');使用
fill: 'yellow', // 选中时的填充色
stroke: 'blue', // 选中时的边框色
lineWidth: 2, // 选中时的边框宽度
shadowColor: '#ff4d4f', // 选中时的阴影颜色
shadowBlur: 10, // 选中时的阴影模糊度
},
// 悬停状态样式
hover: {},
// 高亮状态样式
highlight: {}
},
// 自定义节点动画
animation: {
enable: true,
enter: 'fade',
exit: 'fade'
}
},
edge: {
style: {
stroke: 'gray',
lineWidth: 1, // 线条宽度
lineDash: [5, 5], // 虚线样式,[5, 5] 表示 5 个像素的实线,5 个像素的空格
// border: '2px dashed red',
},
},
// zoomRange: [0.5, 2], // 允许缩小到50%和放大到200%
behaviors: (existingBehaviors) => {
return [
...existingBehaviors,
// 'collapse-expand', // 展开收起,使用图片节点会有问题,组件未兼容
{
type: 'collapse-expand',
align: true, // 确保展开/收起时节点位置正确
trigger: 'click', // 点击触发展开收起
onExpand: (id) => {
console.log('onExpand', id);
async () => {
try {
console.log('开始布局');
// 节点展开完成后触发布局
await graphRef.current.layout({
...layoutConfig,
// animation: {
// duration: 300,
// easing: 'ease-out' // 使用更自然的缓动函数
// }
});
} catch (error) {
console.error('布局失败:', error);
} finally {
// stopRender();
console.log('布局完成');
}
}
},
onCollapse: (node) => {
console.log('onCollapse', node);
}
},
{
type: 'zoom-canvas',
trigger: ['pinch'], // 双指捏合手势
sensitivity: 2, // 降低灵敏度,缩放变化更平缓
// 暂未生效
origin: [599, 199, 0], // Zoom with the viewport center as the origin
},
'drag-element', // 拖动节点或组合
'fix-element-size', // 将元素大小固定为指定值
'hover-activate-neighbors', // 悬停激活相邻节点
'auto-adapt-label', // 自动调整标签位置
// 'optimize-viewport-transform', // 优化视图变换性能,跟focus-element一起用会导致边延迟出现,所以注释掉
// {
// type: 'focus-element', // 快速将关注的节点或边居中显示
// animation: {
// duration: 1800, // 聚焦动画时长,默认值1500
// easing: 'ease-in-out', // 动画缓动函数,默认值ease-in-out
// },
// enable: (event) => {
// // 只对节点启用聚焦,边不聚焦
// // return event.target.type === 'node';
// return true; // 节点或边都启用聚焦
// },
// },
];
},
/**
* 当图初始化完成后(即 new Graph() 之后)执行回调
*/
onInit: (graph) => {
console.log('onInit-图实例已初始化:', graph);
// Before rendering starts
graph.on(GraphEvent.BEFORE_RENDER, () => {
console.log('onInit-图实例开始渲染...');
// Show loading indicator
// showLoadingIndicator();
});
// After rendering completes
graph.on(GraphEvent.AFTER_RENDER, () => {
// 保存图谱实例引用
graphRef.current = graph;
console.log('onInit-图实例渲染完成-图节点:', graph.getData());
});
},
/**
* 当图渲染完成后(即 graph.render() 之后)执行回调
*/
onReady: (graph) => {
console.log('onReady-图实例已渲染:', graph);
// 保存图谱实例引用
graphRef.current = graph;
// const center = graphRef.current?.getCanvasCenter();
// console.log('视口中心坐标:', center);
// 监听节点点击
graph.on(NodeEvent.CLICK, (evt) => {
const { target } = evt;
const nodeId = target.id;
const nodeData = graph.getNodeData(nodeId);
const nodeStates = graph.getElementState(nodeId);
if (nodeId === activeTab) {
// navigate('/stretch/examknowledgemap/');
// 获取单个节点
const node = graph.getNodeData(nodeId);
// console.log('节点位置:', nodeId.style.x, nodeId.style.y);
}
console.log('单击节点数据:', nodeData);
console.log('单击节点状态:', nodeStates);
});
// 布局完成后
graph.on(GraphEvent.AFTER_LAYOUT, () => {
console.log('布局计算完成');
// setGraphLoading(false);
});
// 监听边悬停
// graph.on(EdgeEvent.POINTER_OVER, (evt) => {
// });
},
/**
* 当图销毁后(即 graph.destroy() 之后)执行回调
*/
onDestroy: (graph) => {
console.log('onDestroy-图实例已销毁:', graph);
// stopRender();
},
};
}, [directoryData]);
const MemoizedDendrogram = React.memo(Dendrogram);
```
### Reproduction link / 复现链接
_No response_
### Steps to Reproduce the Bug or Issue / 重现步骤
1. 使用Dendrogram组件
2. 定义options:设置compact为 true、defaultExpandLevel为2、node节点中的style对象属性,里面设置src属性挂上图片。
3. 布局配置
` const layoutConfig = {
type: 'dendrogram', // 布局类型:dendrogram | tree | radial,默认值dendrogram
radial: true, // 是否按照辐射状布局。若 radial 为 true,建议 direction 设置为 LR 或 RL。
direction: 'RL', // 布局方向:LR | RL | TB | BT | H | V,默认值RL(从右到左,根右子左)
preLayout: true,
};`
### Version / 版本
🆕 5.x
### OS / 操作系统
- [x] macOS
- [ ] Windows
- [ ] Linux
- [ ] Others / 其他
### Browser / 浏览器
- [x] Chrome
- [ ] Edge
- [ ] Firefox
- [ ] Safari (Limited support / 有限支持)
- [ ] IE (Nonsupport / 不支持)
- [ ] Others / 其他
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the Dendrogram component and its collapse-expand behavior, using the provided radial dendrogram configuration with image nodes as the reproduction case. Investigate why repeated expansion reaches an undefined image src and verify the result by reproducing the console error in Chrome; done means repeated expansion no longer throws. No source file or test is named in the issue.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- data-visualization, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100