D3和Legend间距中的折线

时间:2016-06-06 16:32:23

标签: javascript css d3.js

我正在使用下面的示例,并希望在饼图外部设置图例,并且还有文本的折线和每个切片的计数和百分比。

使用当前代码,我在Pie中有Pie,当我将鼠标放在切片上时会显示Text和Percentage。

非常感谢帮助。谢谢

有些人请帮忙,因为我无法继续前进。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <link rel="stylesheet" href="normalize.css">
    <style>
        #chart {
            height: 360px;
            margin: 0 auto; /* NEW */
            position: relative;
            width: 360px;
        }

        .tooltip {
            background: #eee;
            box-shadow: 0 0 5px #999999;
            color: #333;
            display: none;
            font-size: 12px;
            left: 130px;
            padding: 10px;
            position: absolute;
            text-align: center;
            top: 95px;
            width: 80px;
            z-index: 10;
        }

        .legend {
            font-size: 12px;
        }

        rect {
            cursor: pointer; /* NEW */
            stroke-width: 2;
        }

            rect.disabled { /* NEW */
                fill: transparent !important; /* NEW */
            }
        /* NEW */
        h1 { /* NEW */
            font-size: 14px; /* NEW */
            text-align: center; /* NEW */
        }
        /* NEW */
    </style>
</head>
<body>

    <div id="chart"></div>
    <script src="Scripts/d3.v3.min.js"></script>
    <script>
      (function(d3) {
        'use strict';

        var width = 360;
        var height = 360;
        var radius = Math.min(width, height) / 2;
        var donutWidth = 75;
        var legendRectSize = 18;
        var legendSpacing = 4;

        var color = d3.scale.category20(); //builtin range of colors

        var svg = d3.select('#chart')
          .append('svg')
          .attr('width', width)
          .attr('height', height)
          .append('g')
          .attr('transform', 'translate(' + (width / 2) +
            ',' + (height / 2) + ')');

        var arc = d3.svg.arc()
          .innerRadius(radius - donutWidth)
          .outerRadius(radius);

        var pie = d3.layout.pie()
          .value(function(d) { return d.count; })
          .sort(null);

        var tooltip = d3.select('#chart')
          .append('div')
          .attr('class', 'tooltip');

        tooltip.append('div')
          .attr('class', 'label');

        tooltip.append('div')
          .attr('class', 'count');

        tooltip.append('div')
          .attr('class', 'percent');

        d3.csv('weekdays.csv', function(error, dataset) {
          dataset.forEach(function(d) {
            d.count = +d.count;
            d.enabled = true;                                         // NEW
          });

          var path = svg.selectAll('path')
            .data(pie(dataset))
            .enter()
            .append('path')
            .attr('d', arc)
            .attr('fill', function(d, i) {
              return color(d.data.label);
            })                                                        // UPDATED (removed semicolon)
            .each(function(d) { this._current = d; });                // NEW

          path.on('mouseover', function(d) {
            var total = d3.sum(dataset.map(function(d) {
              return (d.enabled) ? d.count : 0;                       // UPDATED
            }));
            var percent = Math.round(1000 * d.data.count / total) / 10;
            tooltip.select('.label').html(d.data.label);
            tooltip.select('.count').html(d.data.count);
            tooltip.select('.percent').html(percent + '%');
            tooltip.style('display', 'block');
          });

          path.on('mouseout', function() {
            tooltip.style('display', 'none');
          });

          /* OPTIONAL
          path.on('mousemove', function(d) {
            tooltip.style('top', (d3.event.pageY + 10) + 'px')
              .style('left', (d3.event.pageX + 10) + 'px');
          });
          */

          var legend = svg.selectAll('.legend')
            .data(color.domain())
            .enter()
            .append('g')
            .attr('class', 'legend')
            .attr('transform', function(d, i) {
              var height = legendRectSize + legendSpacing;
              var offset =  height * color.domain().length / 2;
              var horz = -2 * legendRectSize;
              var vert = i * height - offset;
              return 'translate(' + horz + ',' + vert + ')';
            });

          legend.append('rect')
            .attr('width', legendRectSize)
            .attr('height', legendRectSize)
            .style('fill', color)
            .style('stroke', color)                                   // UPDATED (removed semicolon)
            .on('click', function(label) {                            // NEW
              var rect = d3.select(this);                             // NEW
              var enabled = true;                                     // NEW
              var totalEnabled = d3.sum(dataset.map(function(d) {     // NEW
                return (d.enabled) ? 1 : 0;                           // NEW
              }));                                                    // NEW

              if (rect.attr('class') === 'disabled') {                // NEW
                rect.attr('class', '');                               // NEW
              } else {                                                // NEW
                if (totalEnabled < 2) return;                         // NEW
                rect.attr('class', 'disabled');                       // NEW
                enabled = false;                                      // NEW
              }                                                       // NEW

              pie.value(function(d) {                                 // NEW
                if (d.label === label) d.enabled = enabled;           // NEW
                return (d.enabled) ? d.count : 0;                     // NEW
              });                                                     // NEW

              path = path.data(pie(dataset));                         // NEW

              path.transition()                                       // NEW
                .duration(750)                                        // NEW
                .attrTween('d', function(d) {                         // NEW
                  var interpolate = d3.interpolate(this._current, d); // NEW
                  this._current = interpolate(0);                     // NEW
                  return function(t) {                                // NEW
                    return arc(interpolate(t));                       // NEW
                  };                                                  // NEW
                });                                                   // NEW
            });                                                       // NEW

          legend.append('text')
            .attr('x', legendRectSize + legendSpacing)
            .attr('y', legendRectSize - legendSpacing)
            .text(function(d) { return d; });

        });

      })(window.d3);
    </script>
</body>
</html>

2 个答案:

答案 0 :(得分:5)

您可以将图例放在一个组中,然后使用translate

放置图例,从而将图例放在任何您想要的位置

首先制作SVG:

  var s = d3.select('#chart')
    .append('svg')
    .attr('width', width)
    .attr('height', height);

现在制作一个传奇组:

  var legend_group = s.append('g').attr('transform',
    'translate(' + (width / 3) + ',' + (height / 1.4) + ')');

使用将其翻译到您选择的地方。我已将其移至(width/3, height/1.4)

创建一个将绘制饼图的组。

  var svg = s.append('g')
    .attr('transform', 'translate(' + (width / 2) +
      ',' + (radius) + ')');

让我们为每个切片制作一条折线: 此函数将生成与数据集长度一样多的折线。

function makePolyLines() {
  var polyline = svg.selectAll("polyline")
    .data(pie(dataset), key);

  polyline.enter()
    .append("polyline");
  //hide polyline for which value is 0, a case when legend is clicked.
  svg.selectAll("polyline").style("display", function(d) {
    if (d.value == 0) {
      return "none";
    } else {
      return "block";
    }
  });

  polyline.transition().duration(1000)
    .attrTween("points", function(d) {
      this._current = this._current || d;
      var interpolate = d3.interpolate(this._current, d);
      this._current = interpolate(0);
      return function(t) {
        var d2 = interpolate(t);
        var pos = outerArc.centroid(d2);
        pos[0] = radius * 0.95 * (midAngle(d2) < Math.PI ? 1 : -1);
        return [arc.centroid(d2), outerArc.centroid(d2), pos];
      };
    });

  polyline.exit()
    .remove();
}

同样为标签制作文字。

function makeTexts() {
  var text = svg.selectAll(".labels")
    .data(pie(dataset), key);

  text.enter()
    .append("text")
    .attr("dy", ".35em")
    .classed("labels", true)
    .text(function(d) {
      return d.data.label + " (" + d.data.count + ")";
    });
  //hide text for which value is 0, a case when legend is clicked.
  svg.selectAll(".labels").style("display", function(d) {
    if (d.value == 0) {
      return "none";
    } else {
      return "block";
    }
  });

  text.transition().duration(1000)
    .attrTween("transform", function(d) {

      this._current = this._current || d;
      var interpolate = d3.interpolate(this._current, d);
      this._current = interpolate(0);
      return function(t) {
        var d2 = interpolate(t);
        var pos = outerArc.centroid(d2);
        pos[0] = radius * (midAngle(d2) < Math.PI ? 1 : -1);
        return "translate(" + pos + ")";
      };
    })
    .styleTween("text-anchor", function(d) {
      this._current = this._current || d;
      var interpolate = d3.interpolate(this._current, d);
      this._current = interpolate(0);
      return function(t) {
        var d2 = interpolate(t);
        return midAngle(d2) < Math.PI ? "start" : "end";
      };
    });

  text.exit()
    .remove();
}

最后调用这两个函数。

1)最初在获取数据之后。

2)每当点击图例并更新饼图时。

工作代码here

答案 1 :(得分:3)

首先,您需要使svg元素更宽。目前它是var width = 360;,您可以将其更改为var width = 700;

获得更多空间后,确定图例的宽度,例如,让我们使用300px。声明一个新变量:var legendWidth = 300;

现在,当宣布传奇时:

var legend = svg.selectAll('.legend')
    .data(color.domain())
    .enter()
    .append('g')
    .attr('class', 'legend')
    .attr('transform', function(d, i) {
      var height = legendRectSize + legendSpacing;
      var offset =  height * color.domain().length / 2;
      var horz = (-2 * legendRectSize);
      var vert = i * height - offset;
      return 'translate(' + (horz) + ',' + vert + ')';
    });

在计算横向平移时,我们需要考虑legendWidth

var horz = (-2 * legendRectSize) - legendWidth;

注意:您需要修复left元素的top.tooltip CSS属性。

另一个注意事项:如果你想把这个解决方案提升到一个新的水平,你可以用动态的方式实现它,而不是使用&#34;魔术数字&#34; var legendWidth = 300