Ich stecke in d3 (oder JavaScript im Allgemeinen) fest.

Ich möchte mit d3 eine Legende machen. Die Position der 9 Elemente sollte voneinander abhängig sein. Genauer:

Dies ist mein vereinfachtes Array: var dataset = ["Afrika", "Asien", "Karibik", "Mittelamerika", "Europa", "Naher Osten", "Nordamerika", "Ozeanien", "Südamerika"] ;;

Auf der x-Achse möchte ich den nächsten Text 40px weiter (rechts) zeichnen, dann endet die letzte Textbeschriftung. Meine Absicht ist es, jedes Mal den gleichen Abstand zwischen den Kreisen zu haben. Der nächste Text hängt also immer von der Länge des letzten Ländernamens ab.

Ich habe es versucht:

.attr("x", function(d, i) {return i * 40 + d[i-1].length + 7;})

Aber die Konsole sagt, dass d [i-1] undefiniert ist.

Was vermisse ich? Wie würden Sie das lösen?

Vielen Dank im Voraus! Ihre Hilfe wird sehr geschätzt!

Ewa

UPDATE: Eigentlich besteht die Legende, die ich zeichnen möchte, nicht nur aus dem Text, sondern auch aus kleinen Kreisen.

Hier ist das Array (mit fest codiertem x_pos als d [2]: var dataset = [["Afrika", "# 4B7985", 5], ["Asien", "# 58AB86", 55], ["Karibik", "# 63A4B5", 100], ["Central America", "# 818181", 165], ["Europe", "# E9726C", 255], ["Middle East", "# E3AC73", 310], [ "North America", "# B65856", 383], ["Oceania", "# 287E5C", 470], ["South America", "# AC8358", 530];

Wie zeichne ich die Kreise abhängig von der Länge der Ländernamen und erhalte den gleichen Abstand zwischen den Kreisen?

1
Ewa 26 Nov. 2013 im 22:02

3 Antworten

Beste Antwort

Sie können ein Textelement zeichnen, um einen Begrenzungsrahmen auf der Leinwand zu erhalten. Passen Sie dann die Position basierend auf der Breite des letzten Elements an:

svg.selectAll("text")
  .data(data).enter()
  .append("text")
  .attr("x", function(d) {
    return x_pos;
  })
  .attr("y", 50)
  .style("display", "none")
  .text(function(d) { return d; });

svg.selectAll("text")
  .style("display", "block")
  .attr("x", function(d) {
    var c_pos = x_pos;
    x_pos = x_pos + this.getBBox().width + distance;
    return c_pos;
  });

Vollständiges Beispiel: https://vida.io/documents/C5CSjbWLhoJ8rQmhF

4
Phuoc Do 28 Nov. 2013 im 19:42

Zunächst bezieht sich d auf einen einzelnen gebundenen Datenpunkt, während i auf seinen Index im Datensatz verweist. Um den vorherigen Datenpunkt anzuzeigen, müssen Sie auf den Originaldatensatz und nicht auf den bereitgestellten Datenpunkt verweisen.

Angenommen, Sie hatten:

var dataset = ["Africa","Asia", "Caribbean", "Central America", "Europe", "Middle East", "North America", "Oceania", "South America"];

d3.select('.foo').data(dataset)....

Sie möchten Ihre d [i - 1] -Referenzen in Ihrem Positionshandler in Datensatz [i - 1] ändern.

Wenn dies behoben ist, wird Ihr erstes Element immer noch explodieren, da es sich um dataset[0] handelt. dataset[i - 1] ist in diesem Fall dataset[-1].

Sie können Ihre Rückgabeerklärung ändern in:

return i ? (i * 40 + dataset[i-1].length + 7) : i;
0
Gopherkhan 27 Nov. 2013 im 20:13

So würde ich es machen.

//This will be your array of legends
var legendItems = [] 

var legendCount = legendItems.length;
var legendSectionWidth = width / (legendCount+1);
//This will be your "y" value going forward. You can assign anything you want. I have used the following if else case, you should replace it with your logic to calculate y.
var vert = 0;
if(legendPosition == "bottom"){
    if(showAxes)
        vert = height + legendVerticalPad + containerHeight*0.04;
    else
        vert = height + legendVerticalPad + containerHeight*0.02;
}

for(var i = 0; i < legendCount; i++){
    var text = svg.append('text')
        .attr('x', (i+1)*legendSectionWidth)
        .attr('y', vert)
        .attr('class', 'legend-text '+legendItems[i])
        .style('text-anchor', 'middle')
        .style('dominant-baseline', 'central')
        .text(function() {
            return legendItems[i];
        });

    var len = text[0][0].getComputedTextLength();

// you could use circles here, just change the width n height to rx and x and y to cx and cy// you could use circles here, just change the width n height to rx and x and y to cx and cy`enter code here`
//The value 5 is your choice, i use 5px between my rect and text
    svg.append('rect') 
        .attr('x', (i+1)*legendSectionWidth - len/2 - 5 - legendRectSize)
        .attr('y', vert - legendRectSize/2)
        .attr('width', legendRectSize)
        .attr('height', legendRectSize)
        .attr('class', function () { return 'legend '+ legendItems[i];} )
        .attr('label', function()  {
            return legendItems[i]; 
        })
}

Das Ergebnis ist ungefähr so Beispiellegenden

Die folgenden Bilder zeigen, dass die Legenden (Kombination aus Rect und Text) gleich weit voneinander entfernt sind und genau in der Mitte der angegebenen Breite liegen. Und mit dieser Logik werden alle Legenden, unabhängig davon, welche Anzahl von Legenden Sie benötigen, gleich weit voneinander entfernt platziert und genau in der Mitte des Bildschirms angezeigt

5 legends

4 legends

3 legends

2 legends

1 legend

Ich hoffe das hilft.

1
Ws7one 31 März 2016 im 19:04