添加恒定水平线

时间:2021-03-07 23:56:38

标签: svg d3.js

我绘制了一个多折线图,该图根据某些用户输入而变化。我想添加一条参考线以始终出现在值 y=100 处。我能够手动放置一条线,但它并不总是恰好在 y=100。

撇开格式问题不谈,这就是我对一个可能的用户输入的看法。如您所见,参考线略低于 100:

line chart

还有我的代码:

  const svg = d3.select("svg");
  const width = +svg2.attr("width");
  const height = +svg2.attr("height");

  const render = data =>{
    const xValue = d => +d.week;
    const yValue = d => +d.power_score;
    const margin = {top:50, right:70, bottom:60, left:20};
    const innerWidth = width - margin.left - margin.right;
    const innerHeight = height - margin.top - margin.bottom;
  
    const colorValue = d => d.team;
  
    // define scales
    const xScale = d3.scaleLinear()
      .domain([1, d3.max(data, xValue)])
      .range([0, innerWidth-250])
      .nice();
  
    const yScale = d3.scaleLinear()
      .domain([d3.min(data, yValue)-10, d3.max(data, yValue)+10])
      .range([innerHeight, 0])
      .nice();
  
    const colorScale = d3.scaleOrdinal(d3.schemeCategory10);
  
    const g = svg2.append("g")
      .attr('transform', 'translate(75, 50)');
  
    // create axes
    const xAxis = d3.axisBottom(xScale)
      .tickSize(-innerHeight-10);
    
    const yAxis = d3.axisLeft(yScale)
      .tickSize(-innerWidth+240);
  
    const xAxisG = g.append("g").call(xAxis)
      .attr("transform", "translate(0, 400)");
  
    xAxisG.select(".domain")
          .remove();
  
    xAxisG.append("text")
      .attr("class", "axis-label")
      .attr("y", 40)
      .attr("x", (innerWidth-250)/2)
      .attr("fill", "black")
      .text("Week");
  
    const yAxisG = g.append("g").call(yAxis)
      .attr("transform", "translate(-10, 0)")
      .select(".domain")
        .remove();
  
    yAxisG.append("text")
      .attr("class", "axis-label")
      .attr("transform", "rotate(-90)")
      .attr("y", -35)
      .attr("x", -innerHeight/4)
      .attr("fill", "black")
      .text("Power Score");
    
    // generate line
    const lineGenerator = d3.line()
      .x(d => xScale(xValue(d)))
      .y(d => yScale(yValue(d)));
    
    // sort data for legend
    const lastYValue = d =>
      yValue(d.values[d.values.length - 1]);
  
    // group data
    const nested = d3.nest()
      .key(colorValue)
      .entries(data)
      .sort((a, b) =>
          d3.descending(lastYValue(a), lastYValue(b)));
  
    colorScale.domain(nested.map(d => d.key));
    
    // manually add horizonal line here
    svg2.append("g")
     .attr("transform", "translate(75, 267)")
     .append("line")
     .attr("x2", innerWidth-250)
     .style("stroke", "black")
     .style("stroke-width", "2px")
     .style("stroke-dasharray", "3, 3");
    
    // add lines with mouseover effect
    g.selectAll(".line-path").data(nested)
      .enter().append("path")
      .attr("class", "line-path")
      .attr("d", d => lineGenerator(d.values))
      .attr("stroke", d => colorScale(d.key))
      .attr("stroke-width", "3")
      .attr("opacity", "0.5")
      .on("mouseover", function(d, i) {
        d3.select(this).transition()
          .duration("50")
          .attr("stroke-width", "5")
          .attr("opacity", "1")})
      .on("mouseout", function(d, i) {
        d3.select(this).transition()
          .duration("50")
          .attr("stroke-width", "3")
          .attr("opacity", "0.5")});
    
    d3.line()
      .x(d => xScale(xValue(d)))
      .y(d => yScale(yValue(d)));
      
    // draw legend
    const colorLegend = (selection, props) => {
      const {
        colorScale,
        circleRadius,
        spacing,
        textOffset
      } = props;

    const groups = selection.selectAll('g')
      .data(colorScale.domain());
    
    const groupsEnter = groups
      .enter().append('g')
        .attr('class', 'tick');
    
    groupsEnter
      .merge(groups)
        .attr('transform', (d, i) =>
          `translate(0, ${i * spacing})`
        );
    groups.exit().remove();

    groupsEnter.append('circle')
      .merge(groups.select('circle'))
        .attr('r', circleRadius)
        .attr('fill', colorScale);

    groupsEnter.append('text')
      .merge(groups.select('text'))
        .text(d => d)
        .attr('dy', '0.32em')
        .attr('x', textOffset);
    }
    
    svg2.append("g")
      .attr("transform", "translate(710, 60)")
      .call(colorLegend, {
        colorScale,
        circleRadius: 4,
        spacing: 15,
        textOffset: 8
    });
  
    // Title
    g.append("text")
      .attr("class", "title")
      .attr("x", (innerWidth-250)/4)
      .attr("y", -10)
      .text("Weekly Power Ranks");
    
};

d3.csv('data.csv').then(data => {
  data.forEach(d => {
    d.week = +d.week;
    d.power_score = +d.power_score;
  });
    render(data);
});

1 个答案:

答案 0 :(得分:1)

而不是使用幻数...

svg2.append("g")
    .attr("transform", "translate(75, 267)")
    //magic numbers---------------^----^

...使用与绘制路径相同的 y 比例:

g.append("g")
    .attr("transform", `translate(75, ${yScale(100)})`)

此外,将其附加到已翻译的 <g>,或应用相同的翻译(同样,在您的代码中添加更多幻数......)。