多折线图 d3 v6 的 SVG 图例

Posted

技术标签:

【中文标题】多折线图 d3 v6 的 SVG 图例【英文标题】:SVG Legend for multi line chart d3 v6 【发布时间】:2021-10-25 07:29:43 【问题描述】:

我正在尝试为这个多折线图表创建一个图例,这将是一条直线,那么如何根据之前分组的项目宽度计算分组项目的变换转换值。

<g class="legend">
   <g class="legend-group" transform="translate(0, 0)">
      <rect x="0" y="0"   style="fill: red"/>
      <text x="16.79999" y="6" text-anchor="left" style="alignment-baseline: middle">Text 1</text>
   </g>
   <g class="legend-group" transform="translate(120, 0)">
      <rect x="0" y="0"   style="fill: green"/>
      <text x="16.79999" y="6" text-anchor="left" style="alignment-baseline: middle">Text 2</text>
   </g>
   <g class="legend-group" transform="translate(240, 0)">
      <rect x="0" y="0"   style="fill: red"/>
      <text x="16.79999" y="6" text-anchor="left" style="alignment-baseline: middle">Text 3 - Long Texttttt</text>
   </g>
</g>

所以在这里,就像这些文本的长度不同,因此为所有图例组提供固定宽度不起作用,最好的解决方案是什么,这是代码,有没有更好的方法来做到这一点?

let data = this.props,
  size = 12, 
  width = 120;

let legendGroup = select(node)
     .selectAll('.legend-group')
     .data(data)
     .enter()
     .append('g')
     .class('class', 'legend-group')
     .attr('transform', function(d, i) 
        return `translate($width * i, 0)`
     );

     legendGroup
       .append('rect')
       .attr('x', 0)
       .attr('y', 0)
       .attr('width', size)
       .attr('height', size)
       .style('fill', d => d.color);

     legendGroup
        .append('text')
        .attr('x', size * 1.4)
        .attr('y', size/2)
        .text(d => d.name)
        .attr('text-anchor', 'left')
        .style('alignment-baseline', 'middle')

【问题讨论】:

【参考方案1】:

这是一个获取每个组的宽度并使用它来设置位置的示例。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://d3js.org/d3.v7.js"></script>
</head>

<body>
    <div id="chart"></div>

    <script>
      // standard margin convention set up

      const margin =  top: 5, bottom: 5, left: 5, right: 5 ;

      const width = 500 - margin.left - margin.right;
      const height = 500 - margin.top - margin.bottom;

      const svg = d3.select('#chart')
        .append('svg')
          .attr('width', width + margin.left + margin.right)
          .attr('height', height + margin.top + margin.bottom)
          .attr('font-family', 'sans-serif');

      const g = svg.append('g')
          .attr('transform', `translate($margin.left,$margin.top)`);

      // color scale

      const color = d3.scaleOrdinal()
          .domain(['Label 1', 'Much much much longer label 2', 'Label 3'])
          .range(d3.schemeCategory10);

      // color legend
      
      const legend = g.append('g')
          .attr('font-family', 'sans-serif');

      // create one g for each entry in the color scale
      const cell = legend.selectAll('g')
        .data(color.domain())
        .join('g');

      const squareSize = 14;

      // add the colored square for each entry
      cell.append('rect')
          .attr('fill', d => color(d))
          .attr('width', squareSize)
          .attr('height', squareSize)

      // add the text label for each entry
      cell.append('text')
          .attr('dominant-baseline', 'middle')
          .attr('x', squareSize * 1.5)
          .attr('y', squareSize / 2)
          .text(d => d);

      // position the cells
      let xPosition = 0;

      cell.each(function(d, i) 
        d3.select(this)
            .attr('transform', `translate($xPosition)`);
        
        xPosition += (this.getBBox().width + squareSize);
      );
    </script>
</body>
</html>

或者,您也可以使用 HTML 创建图例。然后你可以利用 flexbox 来定位图例条目。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://d3js.org/d3.v7.js"></script>
</head>

<body>
    <div id="chart"></div>

    <script>
      // color scale

      const color = d3.scaleOrdinal()
          .domain(['Label 1', 'Much much much longer label 2', 'Label 3'])
          .range(d3.schemeCategory10);

      // color legend

      // create div for the legend to go in
      const legend = d3.select('#chart')
        .append('div')
          .style('display', 'flex')
          .style('font-family', 'sans-serif');

      // create one div for each entry in the color scale
      const cell = legend.selectAll('div')
        .data(color.domain())
        .join('div')
          .style('margin-right', '1em')
          .style('display', 'flex')
          .style('align-items', 'center');

      // add the colored square for each entry
      cell.append('div')
          .style('background', d => color(d))
          .style('min-width', '14px')
          .style('min-height', '14px')
          .style('margin-right', '0.5em');

      // add the text label for each entry
      cell.append('div')
          .text(d => d);
    </script>
</body>
</html>

或者,如果图例需要在 SVG 元素中,那么您可以将 HTML 放在 &lt;foreignObject&gt; 中。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://d3js.org/d3.v7.js"></script>
</head>

<body>
    <div id="chart"></div>

    <script>
      // standard margin convention set up

      const margin =  top: 5, bottom: 5, left: 5, right: 5 ;

      const width = 500 - margin.left - margin.right;
      const height = 500 - margin.top - margin.bottom;

      const svg = d3.select('#chart')
        .append('svg')
          .attr('width', width + margin.left + margin.right)
          .attr('height', height + margin.top + margin.bottom)
          .attr('font-family', 'sans-serif');

      const g = svg.append('g')
          .attr('transform', `translate($margin.left,$margin.top)`);

      // color scale

      const color = d3.scaleOrdinal()
          .domain(['Label 1', 'Much much much longer label 2', 'Label 3'])
          .range(d3.schemeCategory10);

      // color legend
      
      const legend = g.append('g')
        .append('foreignObject')
          .attr('x', 0)
          .attr('y', 0)
          .attr('width', width)
          .attr('height', 20)
        .append('xhtml:div')
          .style('display', 'flex')
          .style('font-family', 'sans-serif');

      // create one div for each entry in the color scale
      const cell = legend.selectAll('div')
        .data(color.domain())
        .join('div')
          .style('margin-right', '1em')
          .style('display', 'flex')
          .style('align-items', 'center');

      // add the colored square for each entry
      cell.append('div')
          .style('background', d => color(d))
          .style('min-width', '14px')
          .style('min-height', '14px')
          .style('margin-right', '0.5em');

      // add the text label for each entry
      cell.append('div')
          .text(d => d);
    </script>
</body>
</html>

【讨论】:

以上是关于多折线图 d3 v6 的 SVG 图例的主要内容,如果未能解决你的问题,请参考以下文章

d3 v4 带 x 和 y 轴的拖动折线图

Python:如何在绘图中更改多折线图上线条的颜色? [复制]

d3.js 在鼠标悬停时更改折线图点的颜色和大小

Google 折线图图例显示名称

D3-动态更新折线图

ggvis 彩色折线图和相应的图例