You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 01:45:29