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

如何在D3.js中为多线图添加Tooltip并修改线条颜色?

解决D3.js多线图的线条颜色和Tooltip问题

我来帮你搞定这两个需求——给每条线条设置不同颜色,以及添加鼠标悬停显示的Tooltip。


一、修改每条线条的颜色

你现在的代码里所有线条都用了同一个.line类,所以颜色都是默认的steelblue。这里有两种简单的区分方式:

方式1:通过CSS类定义不同颜色

先修改你的CSS样式,为每个线条系列单独定义类:

/* 基础线条样式 */
.line { fill: none; stroke-width: 2px; }
/* Imports线条颜色:橙色 */
.line-imports { stroke: #ff7f0e; }
/* Exports线条颜色:蓝色 */
.line-exports { stroke: #1f77b4; }
/* Added线条颜色:绿色 */
.line-added { stroke: #2ca02c; }

然后在添加线条路径时,给每条线指定对应的类:

// 添加Imports线条
svg.append("path")
  .data([data])
  .attr("class", "line line-imports")
  .attr("d", valueline);

// 添加Exports线条
svg.append("path")
  .data([data])
  .attr("class", "line line-exports")
  .attr("d", valueline2);

// 添加Added线条
svg.append("path")
  .data([data])
  .attr("class", "line line-added")
  .attr("d", valueline3);

方式2:直接在代码中设置stroke属性

如果不想修改CSS,也可以在创建路径时直接指定颜色:

// 添加Imports线条
svg.append("path")
  .data([data])
  .attr("class", "line")
  .attr("stroke", "#ff7f0e")
  .attr("d", valueline);

// 添加Exports线条
svg.append("path")
  .data([data])
  .attr("class", "line")
  .attr("stroke", "#1f77b4")
  .attr("d", valueline2);

// 添加Added线条
svg.append("path")
  .data([data])
  .attr("class", "line")
  .attr("stroke", "#2ca02c")
  .attr("d", valueline3);

二、添加鼠标悬停的Tooltip

要实现Tooltip,我们需要先创建一个隐藏的容器,然后为每个数据点(圆形)绑定鼠标事件来控制Tooltip的显示和内容。

步骤1:创建Tooltip容器

在初始化SVG之后,添加这段代码创建Tooltip:

// 创建Tooltip容器
var tooltip = d3.select("body").append("div")
  .attr("class", "tooltip")
  .style("opacity", 0) // 初始隐藏
  .style("position", "absolute")
  .style("background-color", "#fff")
  .style("border", "1px solid #ddd")
  .style("padding", "8px 12px")
  .style("border-radius", "4px")
  .style("pointer-events", "none") // 避免干扰鼠标交互
  .style("font-size", "14px");

步骤2:添加数据点并绑定Tooltip事件

在添加完线条之后,为每个数据系列添加圆形数据点,并绑定鼠标事件:

// 添加Imports的数据点
svg.selectAll(".point-imports")
  .data(data)
  .enter()
  .append("circle")
  .attr("class", "point-imports")
  .attr("cx", d => x(d.Date))
  .attr("cy", d => y(d.Imports))
  .attr("r", 4)
  .style("fill", "#ff7f0e") // 和线条颜色一致
  .style("cursor", "pointer")
  .on("mouseover", function(d) {
    // 显示Tooltip并填充内容
    tooltip.transition().duration(200).style("opacity", 0.9);
    tooltip.html(`
      <strong>年份:</strong> ${d.Date.getFullYear()}<br>
      <strong>Imports:</strong> ${d.Imports}
    `)
    .style("left", (d3.event.pageX + 10) + "px")
    .style("top", (d3.event.pageY - 30) + "px");
  })
  .on("mouseout", function() {
    // 隐藏Tooltip
    tooltip.transition().duration(300).style("opacity", 0);
  });

// 添加Exports的数据点
svg.selectAll(".point-exports")
  .data(data)
  .enter()
  .append("circle")
  .attr("class", "point-exports")
  .attr("cx", d => x(d.Date))
  .attr("cy", d => y(d.Exports))
  .attr("r", 4)
  .style("fill", "#1f77b4")
  .style("cursor", "pointer")
  .on("mouseover", function(d) {
    tooltip.transition().duration(200).style("opacity", 0.9);
    tooltip.html(`
      <strong>年份:</strong> ${d.Date.getFullYear()}<br>
      <strong>Exports:</strong> ${d.Exports}
    `)
    .style("left", (d3.event.pageX + 10) + "px")
    .style("top", (d3.event.pageY - 30) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition().duration(300).style("opacity", 0);
  });

// 添加Added的数据点
svg.selectAll(".point-added")
  .data(data)
  .enter()
  .append("circle")
  .attr("class", "point-added")
  .attr("cx", d => x(d.Date))
  .attr("cy", d => y(d.added))
  .attr("r", 4)
  .style("fill", "#2ca02c")
  .style("cursor", "pointer")
  .on("mouseover", function(d) {
    tooltip.transition().duration(200).style("opacity", 0.9);
    tooltip.html(`
      <strong>年份:</strong> ${d.Date.getFullYear()}<br>
      <strong>Added:</strong> ${d.added}
    `)
    .style("left", (d3.event.pageX + 10) + "px")
    .style("top", (d3.event.pageY - 30) + "px");
  })
  .on("mouseout", function() {
    tooltip.transition().duration(300).style("opacity", 0);
  });

完整修改后的代码

把以上修改整合到你的代码中,最终代码如下:

<!DOCTYPE html> 
<meta charset="utf-8"> 
<style> 
/* set the CSS */
.line { fill: none; stroke-width: 2px; }
.line-imports { stroke: #ff7f0e; }
.line-exports { stroke: #1f77b4; }
.line-added { stroke: #2ca02c; }
</style> 
<body> 
<!-- load the d3.js library --> 
<script src="https://d3js.org/d3.v4.min.js"></script> 
<script> 
// set the dimensions and margins of the graph 
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// parse the date / time 
var parseTime = d3.timeParse("%Y"); 

// set the ranges 
var x = d3.scaleTime().range([0, width]); 
var y = d3.scaleLinear().range([height, 0]); 

// define the line 
var valueline = d3.line() 
  .x(function(d) { return x(d.Date); }) 
  .y(function(d) { return y(d.Imports); }); 

// define the line 
var valueline2 = d3.line() 
  .x(function(d) { return x(d.Date); }) 
  .y(function(d) { return y(d.Exports); }); 

var valueline3 = d3.line() 
  .x(function(d) { return x(d.Date); }) 
  .y(function(d) { return y(d.added); }); 

var svg = d3.select("body").append("svg") 
  .attr("width", width + margin.left + margin.right) 
  .attr("height", height + margin.top + margin.bottom) 
  .append("g") 
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// 创建Tooltip容器
var tooltip = d3.select("body").append("div")
  .attr("class", "tooltip")
  .style("opacity", 0)
  .style("position", "absolute")
  .style("background-color", "#fff")
  .style("border", "1px solid #ddd")
  .style("padding", "8px 12px")
  .style("border-radius", "4px")
  .style("pointer-events", "none")
  .style("font-size", "14px");

function draw(data, country) { 
  var data = data[country]; 

  // format the data 
  data.forEach(function(d) { 
    d.Date = parseTime(d.Date); 
    d.Imports = +d.Imports; 
    d.Exports = +d.Exports; 
    d.added = +d.added; 
  }); 

  // sort years ascending 
  data.sort(function(a, b){ return a["Date"]-b["Date"]; }) 

  // Scale the range of the data 
  x.domain(d3.extent(data, function(d) { return d.Date; })); 
  y.domain([0, d3.max(data, function(d) { return Math.max(d.Imports, d.Exports,d.added); })]); 

  // Add the valueline path. 
  svg.append("path") 
    .data([data]) 
    .attr("class", "line line-imports") 
    .attr("d", valueline); 

  // Add the valueline path. 
  svg.append("path") 
    .data([data]) 
    .attr("class", "line line-exports") 
    .attr("d", valueline2); 

  svg.append("path") 
    .data([data]) 
    .attr("class", "line line-added") 
    .attr("d", valueline3); 

  // 添加Imports的数据点
  svg.selectAll(".point-imports")
    .data(data)
    .enter()
    .append("circle")
    .attr("class", "point-imports")
    .attr("cx", d => x(d.Date))
    .attr("cy", d => y(d.Imports))
    .attr("r", 4)
    .style("fill", "#ff7f0e")
    .style("cursor", "pointer")
    .on("mouseover", function(d) {
      tooltip.transition().duration(200).style("opacity", 0.9);
      tooltip.html(`
        <strong>年份:</strong> ${d.Date.getFullYear()}<br>
        <strong>Imports:</strong> ${d.Imports}
      `)
      .style("left", (d3.event.pageX + 10) + "px")
      .style("top", (d3.event.pageY - 30) + "px");
    })
    .on("mouseout", function() {
      tooltip.transition().duration(300).style("opacity", 0);
    });

  // 添加Exports的数据点
  svg.selectAll(".point-exports")
    .data(data)
    .enter()
    .append("circle")
    .attr("class", "point-exports")
    .attr("cx", d => x(d.Date))
    .attr("cy", d => y(d.Exports))
    .attr("r", 4)
    .style("fill", "#1f77b4")
    .style("cursor", "pointer")
    .on("mouseover", function(d) {
      tooltip.transition().duration(200).style("opacity", 0.9);
      tooltip.html(`
        <strong>年份:</strong> ${d.Date.getFullYear()}<br>
        <strong>Exports:</strong> ${d.Exports}
      `)
      .style("left", (d3.event.pageX + 10) + "px")
      .style("top", (d3.event.pageY - 30) + "px");
    })
    .on("mouseout", function() {
      tooltip.transition().duration(300).style("opacity", 0);
    });

  // 添加Added的数据点
  svg.selectAll(".point-added")
    .data(data)
    .enter()
    .append("circle")
    .attr("class", "point-added")
    .attr("cx", d => x(d.Date))
    .attr("cy", d => y(d.added))
    .attr("r", 4)
    .style("fill", "#2ca02c")
    .style("cursor", "pointer")
    .on("mouseover", function(d) {
      tooltip.transition().duration(200).style("opacity", 0.9);
      tooltip.html(`
        <strong>年份:</strong> ${d.Date.getFullYear()}<br>
        <strong>Added:</strong> ${d.added}
      `)
      .style("left", (d3.event.pageX + 10) + "px")
      .style("top", (d3.event.pageY - 30) + "px");
    })
    .on("mouseout", function() {
      tooltip.transition().duration(300).style("opacity", 0);
    });

  // Add the X Axis 
  svg.append("g") 
    .attr("transform", "translate(0," + height + ")") 
    .call(d3.axisBottom(x)); 

  // Add the Y Axis 
  svg.append("g") 
    .call(d3.axisLeft(y)); 
} 

// Get the data 
d3.json("data1.json", function(error, data) { 
  if (error) throw error; 
  // trigger render 
  draw(data, "purchase"); 
}); 
</script> 
</body>

内容的提问来源于stack exchange,提问作者White Rose

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:58:28