2012-11-26 226 views
35

我无法将图表图例添加到我的d3js图表中。这是我目前的做法:在D3中添加图表图例

var legend = svg.append("g") 
    .attr("class", "legend") 
    .attr("x", w - 65) 
    .attr("y", 25) 
    .attr("height", 100) 
    .attr("width", 100); 

legend.append("rect") 
    .attr("x", w - 65) 
    .attr("y", 25) 
    .attr("width", 10) 
    .attr("height", 10) 
    .style("fill", function(d) { return color_hash[dataset.indexOf(d)][1] }); 

legend.append("text") 
    .attr("x", w - 65) 
    .attr("y", 25) 
    .text(function(d) { return color_hash[dataset.indexOf(d)][0] + ": " + d; }); 

然后我试图风格的.legend类:

.legend { 
      padding: 5px; 
      font: 10px sans-serif; 
      background: yellow; 
      box-shadow: 2px 2px 1px #888; 
     } 

但我运气不好。

是否有人熟悉将图例添加到图表中能够提供最佳方式?我在网上找不到很多资源。

这里是我的整个图形: http://jsbin.com/ewiwag/2/edit

回答

30

您需要将数据绑定到组成传说中的节点(矩形和文本元素)。

Uncaught TypeError: Cannot read property '1' of undefined 

原因:

目前你想风格的矩形时得到一个错误没有绑定数据

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

注意,D3专注于数据转换和选择操作。因此,首先选择一组节点,然后将数据绑定

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

一旦你enter输入选择,你可以添加节点和动态应用性能。请注意,为避免在其他顶部创建矩形,设置y属性时,必须通过i计数器并将其乘以整数。

/*.....*/ 
     .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; 
     }); 

这里的固定例如:http://jsbin.com/ubafur/3

+0

啊哈,这很合理!一个主要问题是:虽然字体是,.legend的背景和边框样式不适用。我假设可以使用其他任何div的相同方式对元素进行样式设置。这是不正确的? – darko

+0

@ddarko,这是不正确的。使用CSS(选择器)设置SVG元素的样式时,只能使用[SVG属性](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute),而不能使用CSS属性名称。正如[本书](http://chimera.labs.oreilly.com/books/1230000000345/ch03.html#_styling_svg_elements)中所建议的,要区分样式表中的哪些规则是SVG特定的,您可能需要添加'svg '对这些选择器:'svg .legend {...}' –

11

好吧,这里做一个办法:http://jsbin.com/isuris/1/edit

对不起,不得不做出太多改变,能够解释这一切。看看你能否弄清楚。如果您有问题,请在评论中提问,我会修改答案。

<!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

请参阅我对jm-的评论: – darko

+0

不可用,背景和边框不适用CSS用于SVG元素。 SVG的主要样式属性(我的头顶)是stroke,fill,font-family和font-size。您必须为每个图例条目创建另一个'svg:rect',并具有所需的大小,背景和边框。或者,您可以使用HTML制作整个图例,IMO更容易。将所述HTML嵌套在SVG元素中是可能的(我从来没有尝试过),但是你也可以将它制作成兄弟节点,叠加在SVG的顶部。 – meetamit

+0

jsbin的一个稍微修改过的版本(现在通过'push()'添加了点)http://jsbin.com/isuris/437/edit –