2012-11-26 8 views
35

Ho difficoltà ad aggiungere una legenda del grafico al mio grafico d3js. Qui è il mio approccio attuale:Aggiunta di una legenda del grafico in 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; }); 

Poi sto cercando per lo stile della classe .legend:

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

ma non sto avendo molta fortuna.

Qualcuno ha familiarità con l'aggiunta di legende ai grafici in grado di fornire il modo migliore per farlo? Non sto trovando molte risorse per questo online.

Ecco il mio intero grafico: http://jsbin.com/ewiwag/2/edit

risposta

30

È necessario associare i dati ai nodi (rettangoli ed elementi di testo) che compongono la leggenda.

Attualmente si verifica un errore quando si cerca di rettangoli di stile:

Uncaught TypeError: Cannot read property '1' of undefined 

Il motivo: non ci sono dati associati

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

noti che D3 si concentra sulla trasformazione dei dati e opera su selezioni. Quindi, prima selezionare un insieme di nodi e quindi associare i dati

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

Una volta entrati la selezione con enter, è possibile aggiungere i nodi e applicare le proprietà in modo dinamico. Si noti che per evitare di creare rettangoli sopra altri, quando si imposta la proprietà , passare il contatore i e moltiplicarlo per un numero intero.

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

Ecco l'esempio fisso: http://jsbin.com/ubafur/3

+0

Ah ha, questo ha senso! Un grosso problema ancora: sebbene il font sia, gli stili di sfondo e di bordo di .legend non vengono applicati. Suppongo che l'elemento svg possa essere in stile allo stesso modo di qualsiasi altro div. È sbagliato? – darko

+0

@ddarko, che non è corretto. Quando si usano i CSS (selettori) per disegnare gli elementi SVG, è possibile utilizzare solo [attributi SVG] (https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute), non i nomi delle proprietà CSS. Come suggerito in [questo libro] (http://chimera.labs.oreilly.com/books/1230000000345/ch03.html#_styling_svg_elements), per distinguere quali regole del tuo foglio di stile sono specifiche per SVG, potresti voler aggiungere 'svg 'a quei selettori:' svg .legend {...} ' –

11

Ok, ecco un modo per farlo: http://jsbin.com/isuris/1/edit

Siamo spiacenti, ha dovuto fare troppe modifiche per essere in grado di spiegare tutto. Vedi se riesci a capirlo. Se hai domande, chiedi il messaggio nei commenti e modificherai la risposta.

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

Vedere il mio commento a jm- :) – darko

+0

No, lo sfondo e il bordo non sono applicabili CSS per gli elementi SVG. Le principali proprietà stilizzabili di SVG (in cima alla mia testa) sono tratto, riempimento, tipo di carattere e dimensione del carattere. Dovrai creare un altro 'svg: rect' per ogni voce della legenda, con la dimensione, lo sfondo e il bordo desiderati. Oppure puoi rendere l'intera legenda con HTML, che IMO è l'opzione più semplice. Potrebbe essere possibile avere il suddetto HTML nidificato all'interno dell'elemento SVG (non l'ho mai provato), ma si può anche farlo diventare un nodo fratello, sovrapposto a SVG. – meetamit

+0

Una versione leggermente modificata del tuo jsbin (punti ora aggiunti tramite 'push()') http://jsbin.com/isuris/437/edit –