2012-11-26 3 views
35

Ich habe Probleme, eine Diagrammlegende zu meinem d3js Diagramm hinzuzufügen. Hier ist meine aktuelle Ansatz:Hinzufügen einer Diagrammlegende 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; }); 

Dann versuche ich die .legend Klasse, Stil:

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

Aber ich bin nicht viel Glück.

Kennt jemand das Hinzufügen von Legenden zu Diagrammen, die den besten Weg dazu bieten? Ich finde nicht viele Ressourcen für dieses online.

Hier ist meine gesamte Grafik: http://jsbin.com/ewiwag/2/edit

Antwort

30

Sie benötigen Daten an die Knoten zu binden (Rechtecke und Textelemente), die die Legende bilden.

Zur Zeit erhalten Sie einen Fehler, wenn zu Stil Rechtecke versuchen:

Uncaught TypeError: Cannot read property '1' of undefined 

Der Grund: Es gibt keine gebundenen Daten

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

Beachten Sie, dass D3 auf Datentransformation fokussiert und arbeitet auf Auswahl. Also, zuerst eine Menge von Knoten auswählen und dann Daten binden

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

Sobald Sie die Auswahl mit enter eingeben, können Sie Knoten hinzufügen und anwenden Eigenschaften dynamisch. Beachten Sie, dass die y-Eigenschaft den i-Zähler nicht überschreiten darf, um das Erstellen von Rechtecken über anderen zu vermeiden, und es mit einer Ganzzahl multiplizieren.

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

Hier ist das feste Beispiel: http://jsbin.com/ubafur/3

+0

Ah ha, das macht Sinn! Ein großes Problem noch: Obwohl die Schriftart ist, werden die Hintergrund- und Rahmenstile von .legend nicht angewendet. Ich nehme an, das Svg Element kann auf die gleiche Art und Weise gestylt werden wie jedes andere div sein kann. Ist das falsch? – darko

+0

@ddarko, das ist falsch. Bei Verwendung von CSS (Selektoren) zum Formatieren von SVG-Elementen können nur [SVG-Attribute] (https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute) verwendet werden, nicht CSS-Eigenschaftsnamen. Wie in [diesem Buch] vorgeschlagen (http://chimera.labs.oreilly.com/books/1230000000345/ch03.html#_styling_svg_elements), um zu unterscheiden, welche Regeln in Ihrem Stylesheet SVG-spezifisch sind, können Sie 'svg hinzufügen 'zu diesen Selektoren:' svg .legend {...} ' –

11

Ok, hier ist ein Weg, es zu tun: http://jsbin.com/isuris/1/edit

Sorry, hatte zu viele Änderungen vornehmen zu können, alles erklären. Sehen Sie, wenn Sie es herausfinden können. Wenn Sie Fragen haben, fragen Sie die in den Kommentaren und ich werde die Antwort ändern.

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

Siehe meinen Kommentar zu jm- :) – darko

+0

Nein, Hintergrund und Rahmen sind nicht anwendbar CSS für SVG-Elemente. Die wichtigsten styled Eigenschaften von SVG (von der Spitze meines Kopfes) sind Strich, Füllung, Schriftfamilie und Schriftgröße. Sie müssen entweder für jeden Legendeneintrag ein anderes 'svg: rect' mit der gewünschten Größe, Hintergrund und Rahmen erstellen. Oder Sie können die ganze Legende mit HTML erstellen, wobei IMO die leichtere Option ist. Es kann möglich sein, den besagten HTML-Code innerhalb des SVG-Elements zu verschachteln (ich habe das noch nie probiert), aber Sie können ihn auch einfach zu einem gleichgeordneten Knoten machen, der über dem SVG liegt. – meetamit

+0

Eine leicht bearbeitete Version Ihrer jsbin (Punkte jetzt über 'push()' hinzugefügt) http://jsbin.com/isuris/437/edit –