求助:为D3水平条形图添加Tooltip的有效解决方案
极简实现D3水平条形图Tooltip功能
以下是针对你的代码的最简Tooltip实现方案,解决位置偏移问题:
核心修改说明
- 使用固定定位的HTML元素作为Tooltip,避开SVG坐标计算的复杂问题
- 通过D3的
mouseover/mouseout事件控制Tooltip的显示与隐藏,利用鼠标位置动态调整坐标 - 绑定条形数据填充Tooltip内容
修改后的HTML代码
<div class="bar-chart-container"> <svg class="bar-chart"></svg> <!-- 新增Tooltip容器 --> <div class="d3-tooltip" style="position: fixed; opacity: 0; pointer-events: none; background: #333; color: #fff; padding: 8px 12px; border-radius: 4px; font-size: 14px;"></div> </div>
修改后的JavaScript代码
// Bar chart function startBarChartAnimation() { if (isInViewport(document.querySelector(".bar-chart-container"))) { const data = [ { year: "2019", value: 11 }, { year: "2020", value: 10 }, { year: "2021", value: 23 }, { year: "2022", value: 37.4 }, ]; const svg = d3.select(".bar-chart"); const tooltip = d3.select(".d3-tooltip"); // 选中Tooltip容器 const margin = { top: 20, right: 20, bottom: 20, left: 60 }; const width = parseInt(window.getComputedStyle(document.querySelector(".bar-chart-container")).width) - margin.left - margin.right; const height = parseInt(window.getComputedStyle(document.querySelector(".bar-chart-container")).height) - margin.top - margin.bottom; svg.attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); // 渐变定义部分保持不变 const gradient = svg.append("defs") .append("linearGradient") .attr("id", "bar-gradient") .attr("gradientUnits", "userSpaceOnUse") .attr("x1", 0) .attr("y1", 0) .attr("x2", width) .attr("y2", 0); gradient.append("stop") .attr("offset", "0%") .attr("stop-color", "#80bede"); gradient.append("stop") .attr("offset", "100%") .attr("stop-color", "#acd6d9"); const x = d3.scaleLinear() .domain([0, d3.max(data, d => d.value)]) .range([0, width]); const y = d3.scaleBand() .domain(data.map(d => d.year)) .range([0, height]) .padding(0.3); const g = svg.append("g") .attr("transform", `translate(${margin.left},${margin.top})`); // 创建条形并添加Tooltip事件监听 g.selectAll(".bar") .data(data) .enter() .append("rect") .attr("class", "bar") .attr("y", d => y(d.year)) .attr("height", y.bandwidth()) .attr("fill", "url(#bar-gradient)") .attr("width", 0) .transition() .duration((d, i) => 500 + i * 200) .attr("width", d => x(d.value)) // 鼠标悬停显示Tooltip .on("mouseover", function(event, d) { tooltip.transition().duration(200).style("opacity", .9); tooltip.html(`年份: ${d.year}<br>数值: ${d.value}`) .style("left", (event.pageX + 10) + "px") .style("top", (event.pageY - 28) + "px"); }) // 鼠标移出隐藏Tooltip .on("mouseout", function() { tooltip.transition().duration(500).style("opacity", 0); }); // Y轴设置保持不变 g.append("g") .call(d3.axisLeft(y)) .call(g => g.selectAll("line").remove()) .call(g => g.selectAll("path").remove()) .call(g => g.selectAll("text") .style("fill", "#bbbbbb") ); window.removeEventListener("scroll", startBarChartAnimation); } } window.addEventListener("scroll", startBarChartAnimation); // 补充视口检测函数(若你原有此函数可忽略) function isInViewport(element) { const rect = element.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }
内容的提问来源于stack exchange,提问作者kodikai
相关产品推荐
相关产品推荐

