求助:用D3.js复现带Y轴悬浮提示的分组连接散点图
解决D3.js连接散点图的两个问题:分组Y轴 + Y轴tooltip
一、处理分组Y轴数值
如果你的数据按类别分组(不同组别对应Y轴上的独立区块),可以用嵌套比例尺实现分组布局:
- 提取并去重所有分组名称:
const groups = [...new Set(data.map(d => d.group))];
- 定义分组主比例尺(控制每个组在Y轴的整体位置):
const yGroupScale = d3.scaleBand() .domain(groups) .range([height, 0]) // height为图表总高度,坐标从下到上 .padding(0.3); // 组与组之间的间距
- 若每个分组内有多个数据点,定义组内子比例尺(控制组内点的垂直分布):
// 先计算每个分组的最大数据量 const maxPointsPerGroup = d3.max(groups.map(g => data.filter(d => d.group === g).length)); const ySubScale = d3.scalePoint() .domain(d3.range(maxPointsPerGroup)) // 用索引作为定义域 .range([0, yGroupScale.bandwidth()]) // 范围限制在当前组的带宽内 .padding(0.1);
- 绘制散点时计算Y坐标:
svg.selectAll('.dot') .data(data) .enter() .append('circle') .attr('class', 'dot') .attr('cx', d => xScale(d.xValue)) .attr('cy', d => { // 获取当前点在组内的索引 const groupIndex = data.filter(item => item.group === d.group).indexOf(d); return yGroupScale(d.group) + ySubScale(groupIndex); }) .attr('r', 5);
- 基于分组比例尺绘制Y轴:
const yAxis = d3.axisLeft(yGroupScale); svg.append('g') .attr('transform', `translate(${margin.left},0)`) .call(yAxis);
二、实现鼠标悬停沿Y轴的tooltip效果
这里实现「鼠标移动时显示水平辅助线+Y轴旁提示框」的效果,也支持散点悬停触发:
方案1:鼠标全局移动触发
- 创建辅助线和tooltip元素:
// 水平辅助线(初始隐藏) const hoverLine = svg.append('line') .attr('class', 'hover-line') .attr('stroke', '#999') .attr('stroke-width', 1) .attr('stroke-dasharray', '4,2') .style('opacity', 0); // 提示框(用div实现,初始隐藏) const tooltip = d3.select('body').append('div') .attr('class', 'tooltip') .style('position', 'absolute') .style('background', 'rgba(0,0,0,0.8)') .style('color', '#fff') .style('padding', '8px 12px') .style('border-radius', '4px') .style('pointer-events', 'none') .style('opacity', 0);
- 监听svg鼠标事件更新元素:
svg.on('mousemove', function(event) { const [, mouseY] = d3.pointer(event); // 将鼠标Y坐标转换为对应的分组 const currentGroup = yGroupScale.invert(mouseY); // 更新辅助线位置 hoverLine .style('opacity', 1) .attr('x1', margin.left) .attr('x2', width + margin.left) .attr('y1', mouseY) .attr('y2', mouseY); // 更新tooltip内容与位置 tooltip .style('opacity', 1) .html(`当前分组: ${currentGroup}`) .style('left', `${margin.left - 110}px`) // 定位到Y轴左侧 .style('top', `${mouseY - 15}px`); }) .on('mouseout', function() { hoverLine.style('opacity', 0); tooltip.style('opacity', 0); });
方案2:悬停在散点上触发
把事件监听绑定到散点元素即可:
svg.selectAll('.dot') .on('mouseover', function(event, d) { const dotY = d3.select(this).attr('cy'); // 显示辅助线到当前散点的Y位置 hoverLine .style('opacity', 1) .attr('y1', dotY) .attr('y2', dotY); tooltip .style('opacity', 1) .html(`分组: ${d.group}<br>数值: ${d.xValue}`) .style('left', `${event.pageX + 10}px`) .style('top', `${event.pageY - 20}px`); }) .on('mouseout', function() { hoverLine.style('opacity', 0); tooltip.style('opacity', 0); });
内容的提问来源于stack exchange,提问作者Ioana Ionescu
相关产品推荐
相关产品推荐

