Adding a chart legend in D3

后端 未结 2 1650
温柔的废话
温柔的废话 2020-12-29 18:29

I am having trouble adding a chart legend to my d3js chart. Here is my current approach:

var legend = svg.append(\"g\")
  .attr(\"class\", \"legend\")
  .att         


        
相关标签:
2条回答
  • 2020-12-29 18:50

    Ok, here's one way to do it: http://jsbin.com/isuris/1/edit

    Sorry, had to make too many changes to be able to explain it all. See if you can figure it out. If you have questions, ask the in the comments and I'll modify the answer.

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
        <style type="text/css">
    
          .axis path,
          .axis line {
            fill: none;
            stroke: black;
            shape-rendering: crispEdges;
          }
    
          .axis text {
            font-family: sans-serif;
            font-size: 11px;
          }
    
          .y1 {
            fill: white;
            stroke: orange;
            stroke-width: 1.5px;
          }
    
          .y2 {
            fill: white;
            stroke: red;
            stroke-width: 1.5px;
          }
    
          .y3 {
            fill: white;
            stroke: steelblue;
            stroke-width: 1.5px;
          }
    
          .line {
            fill: none;
            stroke-width: 1.5px;
          }
    
          div.tooltip {
                  position: absolute;
                  text-align: center;
                  width: 50px;
                  height: 10px;
                  padding: 5px;
                  font: 10px sans-serif;
                  background: whiteSmoke;
                  border: solid 1px #aaa;
                  pointer-events: none;
                  box-shadow: 2px 2px 1px #888;
                }
    
                .legend {
                  padding: 5px;
                  font: 10px sans-serif;
                  background: yellow;
                  box-shadow: 2px 2px 1px #888;
                }
    
                .title {
                  font: 13px sans-serif;
                }
    
        </style>
      </head>
      <body>
        <script type="text/javascript">
    
        //Width and height
        var w = 500;
        var h = 300;
        var padding = 50;
    
        var now = d3.time.hour.utc(new Date);
        var dataset = [ [ ],[ ] ];
        dataset[0].push({x: d3.time.hour.utc.offset(now, -5), y: 0});
        dataset[0].push({x: d3.time.hour.utc.offset(now, -4), y: 0});
        dataset[0].push({x: d3.time.hour.utc.offset(now, -3), y: 2});
        dataset[0].push({x: d3.time.hour.utc.offset(now, -2), y: 0});
        dataset[0].push({x: d3.time.hour.utc.offset(now, -1), y: 0});
        dataset[0].push({x: now, y: 0});
    
        dataset[1].push({x: d3.time.hour.utc.offset(now, -5), y: 3});
        dataset[1].push({x: d3.time.hour.utc.offset(now, -4), y: 1});
        dataset[1].push({x: d3.time.hour.utc.offset(now, -3), y: 3});
        dataset[1].push({x: d3.time.hour.utc.offset(now, -2), y: 1});
        dataset[1].push({x: d3.time.hour.utc.offset(now, -1), y: 5});
        dataset[1].push({x: now, y: 1});
    
        var color_hash = {  0 : ["apple", "green"],
                  1 : ["mango", "orange"],
                  2 : ["cherry", "red"]
                }                      
    
        // Define axis ranges & scales        
        var yExtents = d3.extent(d3.merge(dataset), function (d) { return d.y; });
        var xExtents = d3.extent(d3.merge(dataset), function (d) { return d.x; });
    
      var xScale = d3.time.scale()
             .domain([xExtents[0], xExtents[1]])
             .range([padding, w - padding * 2]);
    
      var yScale = d3.scale.linear()
             .domain([0, yExtents[1]])
             .range([h - padding, padding]);
    
    
      // Create SVG element
      var svg = d3.select("body")
          .append("svg")
          .attr("width", w)
          .attr("height", h);
    
    
      // Define lines
      var line = d3.svg.line()
             .x(function(d) { return x(d.x); })
             .y(function(d) { return y(d.y1, d.y2, d.y3); });
    
      var pathContainers = svg.selectAll('g.line')
      .data(dataset);
    
      pathContainers.enter().append('g')
      .attr('class', 'line')
      .attr("style", function(d) {
        return "stroke: " + color_hash[dataset.indexOf(d)][1]; 
      });
    
      pathContainers.selectAll('path')
      .data(function (d) { return [d]; }) // continues the data from the pathContainer
      .enter().append('path')
        .attr('d', d3.svg.line()
          .x(function (d) { return xScale(d.x); })
          .y(function (d) { return yScale(d.y); })
        );
    
      // add circles
      pathContainers.selectAll('circle')
      .data(function (d) { return d; })
      .enter().append('circle')
      .attr('cx', function (d) { return xScale(d.x); })
      .attr('cy', function (d) { return yScale(d.y); })
      .attr('r', 3); 
    
        //Define X axis
      var xAxis = d3.svg.axis()
              .scale(xScale)
              .orient("bottom")
              .ticks(5);
    
      //Define Y axis
      var yAxis = d3.svg.axis()
              .scale(yScale)
              .orient("left")
              .ticks(5);
    
      //Add X axis
      svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + (h - padding) + ")")
      .call(xAxis);
    
      //Add Y axis
      svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(" + padding + ",0)")
      .call(yAxis);
    
      // Add title    
      svg.append("svg:text")
           .attr("class", "title")
         .attr("x", 20)
         .attr("y", 20)
         .text("Fruit Sold Per Hour");
    
    
      // add legend   
      var legend = svg.append("g")
        .attr("class", "legend")
        .attr("x", w - 65)
        .attr("y", 25)
        .attr("height", 100)
        .attr("width", 100);
    
      legend.selectAll('g').data(dataset)
          .enter()
          .append('g')
          .each(function(d, i) {
            var g = d3.select(this);
            g.append("rect")
              .attr("x", w - 65)
              .attr("y", i*25)
              .attr("width", 10)
              .attr("height", 10)
              .style("fill", color_hash[String(i)][1]);
    
            g.append("text")
              .attr("x", w - 50)
              .attr("y", i * 25 + 8)
              .attr("height",30)
              .attr("width",100)
              .style("fill", color_hash[String(i)][1])
              .text(color_hash[String(i)][0]);
    
          });
        </script>
      </body>
    </html>
    
    0 讨论(0)
  • 2020-12-29 19:14

    You need to bind data to the nodes (rectangles and text elements) that make up the legend.

    Currently you get an error when trying to style rectangles:

    Uncaught TypeError: Cannot read property '1' of undefined 
    

    The reason: there's no bound data

    legend.append("rect")
          /*...*/
          .style("fill", function(d) { 
             // d <---- is undefined
             return color_hash[dataset.indexOf(d)][1] 
          });
    

    Notice that D3 focuses on data transformation and operates on selections. So, first select a set of nodes and then bind data

    legend.selectAll('rect')
          .data(dataset)
          .enter()
    

    Once you enter the selection with enter, you can add nodes and apply properties dynamically. Notice that to avoid creating rectangles on top of others, when setting the y property pass the i counter and multiply it by an integer.

      /*.....*/
          .append("rect")
          .attr("x", w - 65)
          .attr("y", function(d, i){ return i *  20;})
          .attr("width", 10)
          .attr("height", 10)
          .style("fill", function(d) { 
             var color = color_hash[dataset.indexOf(d)][1];
             return color;
          });
    

    Here's the fixed example: http://jsbin.com/ubafur/3

    0 讨论(0)
提交回复
热议问题