2012-11-26 15 views
35

Mam problem z dodaniem legendy wykresu do mojego wykresu d3js. Oto moje obecne podejście:Dodawanie legendy wykresu w 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; }); 

to jestem próby styl klasę .legend:

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

Ale nie mam dużo szczęścia.

Czy ktoś jest zaznajomiony z dodawaniem legend do wykresów, które są w stanie zapewnić najlepszy sposób na zrobienie tego? Nie znajduję wielu zasobów do tego online.

Oto cały mój wykres: http://jsbin.com/ewiwag/2/edit

Odpowiedz

30

Trzeba powiązać dane do węzłów (prostokątów i elementów tekstowych), które tworzą legendę.

Obecnie pojawi się błąd podczas próby prostokątów styl:

Uncaught TypeError: Cannot read property '1' of undefined 

Powód: nie ma danych bound

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

Uwaga, D3 koncentruje się na transformacji danych i działa na selekcji. Więc najpierw wybrać zestaw węzłów, a następnie powiązać dane

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

Po wpisaniu wybór z enter, można dodać węzłów i zastosowanie właściwości dynamiczne. Zauważ, że aby uniknąć tworzenia prostokątów na innych, podczas ustawiania właściwości y należy przekazać licznik i i pomnożyć go przez liczbę całkowitą.

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

Oto stałym przykład: http://jsbin.com/ubafur/3

+0

Ah ha, to ma sens! Jeszcze jedna ważna kwestia: chociaż czcionka jest, style tła i obramowania .legend nie są stosowane. Zakładam, że element svg może być stylizowany w taki sam sposób jak dowolny inny element div. Czy to jest nieprawidłowe? – darko

+0

@ddarko, to jest nieprawidłowe. Używając CSS (selektory) do stylizowania elementów SVG, można używać tylko [atrybutów SVG] (https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute), a nie nazw właściwości CSS. Jak zasugerowano w [tej książce] (http://chimera.labs.oreilly.com/books/1230000000345/ch03.html#_styling_svg_elements), aby odróżnić, które reguły w twoim arkuszu stylów są specyficzne dla SVG, możesz dodać 'svg 'do tych selektorów:' svg .legend {...} ' –

11

Ok, oto jeden ze sposobów, aby to zrobić: http://jsbin.com/isuris/1/edit

Niestety, musiał dokonać zbyt wielu zmian, aby móc to wszystko wyjaśnić. Sprawdź, czy potrafisz to rozgryźć. Jeśli masz pytania, poproś o komentarz w komentarzach, a ja zmienię odpowiedź.

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

Zobacz mój komentarz do jm- :) – darko

+0

Nie, tło i ramka nie mają zastosowania CSS dla elementów SVG. Główne stylowe właściwości SVG (z góry mojej głowy) to: obrys, wypełnienie, rodzina czcionek i rozmiar czcionki. Będziesz musiał albo utworzyć inny 'svg: rect' dla każdego wpisu legendy, z żądanym rozmiarem, tłem i ramką. Lub możesz stworzyć całą legendę za pomocą HTMLa, którego IMO jest łatwiejszą opcją. Możliwe, że wspomniany HTML jest zagnieżdżony wewnątrz elementu SVG (nigdy tego nie próbowałem), ale równie dobrze możesz sprawić, że będzie to węzeł równorzędny, nałożony na SVG. – meetamit

+0

Lekko zredagowana wersja twojego jsbin (punkty dodane teraz przez 'push()') http://jsbin.com/isuris/437/edit –

Powiązane problemy