前言
这一小节来讲一下如何实现血缘高亮,实现血缘高亮有如下几个步骤:
1. 定位事件触发字段
鼠标点击 table 字段,如何定位点击的是那个字段?
解决思路:利用图元的 name 属性,AntV G6 官网建议每个图元都加上 name 属性,name 的值是一个字符串,
所以我们可以在构建表字段的时候为 name 赋上我们的字段名
if (attrs) {
attrs.forEach((e: any, i: any) => {
const { key } = e;
// group部分图形控制
listContainer.addShape('rect', {
attrs: {
x: 0,
y: i * itemHeight + itemHeight,
fill: '#ffffff',
width: width,
height: itemHeight,
cursor: 'pointer',
},
name: key,
draggable: true,
});
// group文本控制
listContainer.addShape('text', {
attrs: {
x: fontOffsetX,
y: (i + 1) * itemHeight + fontOffsetY,
text: handleLabelLength(key),
fontSize: fontSize,
fill: '#000',
fontWeight: 500,
cursor: 'pointer',
},
name: key,
});
});
}
上面仅为代码片段,完整请看 github.com/lijunping36…
注意 name 的值即可。这样就可以在事件触发时定位到触发的是哪个字段了
// 监听节点点击事件
graph.off('node:click').on('node:click', (evt: any) => {
console.log('node:click');
const { item, target } = evt;
const name = target.get('name');
if (!name) return;
if (fieldCheckedRef.current) {
handleNodeEvent(graph, item, name);
} else {
handleNodeTableEvent(graph, item, name);
}
});
2. 寻找触发字段的所有来源和目标
上面我们已经定位到触发字段,接着我们需要根据定位到的字段寻找它的上下游字段及连线以实现高亮,
我们可以顺着连线去找,我们知道 edge 都有来源和目标,所以可以分为左右两个方向去找
/**
* 获取选中 label 的所有左关联边
* @param edges node 的所有 edges
* @param model node 的 model
* @param sourceAnchor 选中的 label
* @param leftActiveEdges 左关联边集合
*/
export const getLeftRelation = (
edges: any[],
model: any,
sourceAnchor: any,
leftActiveEdges: any[]
) => {
const source = model['id']; // 当前节点
edges
.filter((edge: any) => !leftActiveEdges.includes(edge))
.forEach((edge: any) => {
if (
edge.getModel()['target'] === source &&
edge.getModel()['targetAnchor'] === sourceAnchor
) {
leftActiveEdges.push(edge);
const currentNode = edge.getSource();
const currentModel = currentNode.getModel();
const currentEdges = currentNode.getInEdges();
const currentSourceAnchor = edge.getModel()['sourceAnchor'];
getLeftRelation(
currentEdges,
currentModel,
currentSourceAnchor,
leftActiveEdges
);
}
});
};
/**
* 获取选中 label 的所有右关联边
* @param edges node 的所有 edges
* @param model node 的 model
* @param sourceAnchor 选中的 label
* @param rightActiveEdges 右关联边集合
*/
export const getRightRelation = (
edges: any[],
model: any,
sourceAnchor: any,
rightActiveEdges: any[]
) => {
const source = model['id']; // 当前节点
edges
.filter((edge: any) => !rightActiveEdges.includes(edge))
.forEach((edge: any) => {
if (
edge.getModel()['source'] === source &&
edge.getModel()['sourceAnchor'] === sourceAnchor
) {
rightActiveEdges.push(edge);
const currentNode = edge.getTarget();
const currentModel = currentNode.getModel();
const currentEdges = currentNode.getOutEdges();
const currentTargetAnchor = edge.getModel()['targetAnchor'];
getRightRelation(
currentEdges,
currentModel,
currentTargetAnchor,
rightActiveEdges
);
}
});
};
3. 对寻找到的所有字段和连线进行高亮
在上一步中,我们找到了事件触发的字段以及字段的所有来源和目标字段及连线,接下来就是对找到的这些字段和连线进行高亮
字段高亮:将字段字体加粗
连线高亮:将连线颜色改为其他颜色
要实现高亮,那就要动态修改元素的样式(状态),通过阅读 AntV G6 的文档,知道了通过动态设置元素的 state 可以实现我们想要的效果
关键点:在修改状态的时候要拼接上要高亮的字段,这样在处理高亮的时候就可以知道要操作哪个字段了
/**
* 设置左边关联节点及边状态
* @param graph
* @param edges 连线
* @param color 连线高亮颜色
* @param name 状态名称
*/
export const setLeftStats = (
graph: any,
edges: any[],
color: string,
name: string
) => {
if (!graph) return;
edges.forEach(function (edge: any) {
graph.setItemState(edge, `highlight-${color}`, true);
edge.toFront();
const sourceAnchor = edge.getModel()['sourceAnchor'];
graph.setItemState(edge.getSource(), name + '-' + sourceAnchor, true);
});
};
处理字段高亮代码如下,在设置高亮时截取到高亮字段,找到高亮元素进行高亮即可,注意这里是通过 keyShape 去查找的
setState(name, value, item: any) {
// 字段高亮
if (name && name.startsWith('highlight')) {
const anchor = name.split('-')[1];
const shape = item.get('keyShape');
// 查找 label 下标
const anchorIndex = item.getModel().attrs.findIndex((e: any) => e.key === anchor);
// 查找 label 元素,通过下标来找
const label = shape.get('parent').get('children')[3].get('children')[
anchorIndex * 2 + 1
];
if (value) {
//label.attr('fill', '#A3B1BF');
//label.attr('fill', 'red');
label.attr('fontWeight', 800);
} else {
//label.attr('fill', '#A3B1BF');
//label.attr('fill', 'red');
label.attr('fontWeight', 500);
}
}
}
处理连线高亮代码如下:
/**
* 设置状态,主要用于高亮
* @param name 状态
* @param value true | false
* @param item 要改变状态的边
*/
setState(name, value, item: any) {
const shape = item.get('keyShape');
// 字段连线高亮或表连线高亮
if (name && name.startsWith('highlight')) {
const highlightColor = name.split('-')[1];
if (value) {
//shape.attr('opacity', 0.2);
shape.attr('stroke', highlightColor);
shape.attr('lineWidth', 3);
} else {
//shape.attr('opacity', 1);
shape.attr('stroke', '#6C6B6B');
shape.attr('lineWidth', 2);
}
}
}