散点图图例切换时如何同步移除关联注释与气泡文本?
散点图图例切换时同步隐藏关联注释与气泡文本的实现方案
我制作了带图例、文本和注释的散点图,但点击图例切换系列显示/隐藏时,气泡关联的文本和注释不会跟着消失。以下是当前生成注释的代码,求实现点击图例按钮时同步移除对应注释和气泡文本的方法:
const getAnnotations = () => { if (totalRecords <= 10) { const annotations: any[] = []; const placedPositions: { x: number; y: number }[] = []; const groupedData = new Map<string, { storeLabels: string[], x: number, y: number, z: number }>(); // 按total_sales和spearman_correlation分组数据 data.forEach((item) => { const storeLabel = item[firstColumnFieldName] || "Unknown Store"; const key = `${item.total_sales}-${item.spearman_correlation}`; if (!groupedData.has(key)) { groupedData.set(key, { storeLabels: [], x: item.total_sales, y: item.spearman_correlation, z: item.max_sales }); } groupedData.get(key)!.storeLabels.push(storeLabel); }); // 交替使用正负Y轴偏移量 let isPositiveYOffset = true; groupedData.forEach(({ storeLabels, x, y, z }) => { // 仅保留相关系数超出±0.5的注释 if (y <= 0.5 && y >= -0.5) return; const combinedLabel = storeLabels.join(", <br>"); const isShortLabel = combinedLabel.length <= 3; // 计算气泡大小 const bubbleSize = calculateBubbleSize(y, totalRecords, x, z, allLessThanOrEqualZero); // 判断标签是否能放入气泡内 const labelFitsInsideBubble = !isLabelTooBig(combinedLabel, bubbleSize); const yOffset = isPositiveYOffset ? 30 : -30; isPositiveYOffset = !isPositiveYOffset; // 检查注释是否与已有气泡/注释重叠 const isOverlapping = (x: number, y: number, bubbleSize: number) => { const annotationOverlap = placedPositions.some(pos => { const distance = calculateDistance(pos.x, pos.y, x, y); return distance < bubbleSize * 0.8; }); const bubbleOverlap = placedPositions.some(pos => { const distance = calculateDistance(pos.x, pos.y, x, y); return distance < bubbleSize; }); return annotationOverlap || bubbleOverlap; }; let currentX = x; let currentY = y + yOffset; if (isOverlapping(currentX, currentY, bubbleSize)) { return; } placedPositions.push({ x: currentX, y: currentY }); annotations.push({ x: x, y: y, text: `<b>${combinedLabel}</b>`, showarrow: !labelFitsInsideBubble, arrowhead: 1, arrowwidth: 2, ax: labelFitsInsideBubble ? 10 : 30, ay: labelFitsInsideBubble ? yOffset : -yOffset, font: { size: labelFitsInsideBubble ? 12 : 10, color: labelFitsInsideBubble ? "white" : "black", weight: "800" }, bgcolor: labelFitsInsideBubble ? "transparent" : "white", bordercolor: labelFitsInsideBubble ? "transparent" : "black", borderradius: 10, xanchor: isShortLabel ? "center" : undefined, yanchor: isShortLabel ? "middle" : undefined, arrowcolor: "black", }); }); return annotations; } return []; };
实现方案
要让图例切换时同步隐藏对应注释和气泡文本,核心是关联图例选中状态与注释/文本的显示逻辑,以下是两种直接可行的方式:
方式1:基于可见系列过滤注释(推荐)
维护可见系列状态:在组件中声明状态变量,记录当前显示的系列名称集合:
const [visibleSeries, setVisibleSeries] = useState(new Set<string>(/* 初始可见系列名称 */));监听图例点击事件:绑定图表的图例点击事件,更新可见系列集合(以Plotly.js为例,其他图表库逻辑类似):
chart.current.on('legendclick', (eventData) => { const seriesName = eventData.data[eventData.curveNumber].name; const newVisibleSeries = new Set(visibleSeries); if (newVisibleSeries.has(seriesName)) { newVisibleSeries.delete(seriesName); } else { newVisibleSeries.add(seriesName); } setVisibleSeries(newVisibleSeries); });修改注释生成逻辑:在
getAnnotations中加入系列可见性过滤,只生成当前可见系列的注释:- 分组时保留每个数据项对应的系列名称(假设你的
data数组每个item有seriesName字段); - 遍历分组数据时,先判断当前系列是否在可见集合中,不在则跳过:
修改后的关键代码片段:
const getAnnotations = () => { if (totalRecords <= 10) { const annotations: any[] = []; const placedPositions: { x: number; y: number }[] = []; const groupedData = new Map<string, { storeLabels: string[], x: number, y: number, z: number, series: string // 新增:存储系列名称 }>(); // 分组时保留系列信息 data.forEach((item) => { const storeLabel = item[firstColumnFieldName] || "Unknown Store"; const key = `${item.total_sales}-${item.spearman_correlation}`; if (!groupedData.has(key)) { groupedData.set(key, { storeLabels: [], x: item.total_sales, y: item.spearman_correlation, z: item.max_sales, series: item.seriesName // 绑定对应系列 }); } groupedData.get(key)!.storeLabels.push(storeLabel); }); let isPositiveYOffset = true; groupedData.forEach(({ storeLabels, x, y, z, series }) => { // 新增:如果系列不可见,直接跳过 if (!visibleSeries.has(series)) return; // 原有的注释生成逻辑保持不变... }); return annotations; } return []; };- 分组时保留每个数据项对应的系列名称(假设你的
同步气泡文本:气泡文本的处理逻辑和注释一致,生成气泡数据时只保留
visibleSeries中的系列项即可。
方式2:动态切换注释可见性
如果不想重新生成注释,可以给每个注释添加visible属性,通过图例点击事件直接切换:
- 生成注释时绑定系列标识:
annotations.push({ // 原有的注释属性... customdata: { series: item.seriesName }, // 绑定所属系列 visible: true // 初始可见 }); - 监听图例点击更新可见性:
chart.current.on('legendclick', (eventData) => { const seriesName = eventData.data[eventData.curveNumber].name; const updatedAnnotations = annotations.map(anno => { if (anno.customdata?.series === seriesName) { return {...anno, visible: !anno.visible}; } return anno; }); setAnnotations(updatedAnnotations); // 气泡文本同理,更新对应项的可见状态 });
内容的提问来源于stack exchange,提问作者vishnu BHAT
相关产品推荐
相关产品推荐

