Frage d3.js Stapelbar mit umschaltbarer Serie


dieses Mal versuche ich, eine gestapelte Bar mit toggleable-Serie zu schaffen - basierend auf Mike Bostocks Beispiel (danke noch einmal Mike!) Ich habe es bereits geschafft, es reaktionsfähig und zoombar zu machen, und die umschaltbare Serie durch eine Legende ist das Letzte, was übrig bleibt.

Ich habe die Legendenelemente erstellt und die richtige Farbe mithilfe von Schlüsseln angewendet:

var legendItem = d3.select(".legend")
  .selectAll("li")
  .data(keys)
  .enter()
  .append("li")
  .on('click', function(d) {
    keys.forEach(function(c) {
      if (c != d) tKeys.push(c)
    });
    fKeys = tKeys;
    tKeys = [];
    redraw();
  });

legendItem
  .append("span")
  .attr("class", "color-square")
  .style("color", function(d) {
    return colorScale5(d);
  });

legendItem
  .append("span")
  .text(function(d) {
    return (d)
  });

Basierend auf der Struktur, um das toggleable Element zu erstellen, kam ich zu dem Schluss, dass ich irgendwie in der Lage sein muss, es von den Tasten AND das Dataset umzuschalten - oder gibt es eine andere Möglichkeit, es zu tun? Ich habe es geschafft, einen bestimmten Schlüssel aus den Schlüsseln zu entfernen, aber nicht aus dem Datensatz, ich habe keine Ahnung, wie man ihn richtig abbildet.

Das zweite Problem ist, dass ich keine Möglichkeit finde, einen Schlüssel umzuschalten, sondern ihn einfach zu entfernen. Dies ist der ursprüngliche Datensatz:

var data = [{
  "country": "Greece",
  "Vodafone": 57,
  "Wind": 12,
  "Cosmote": 20
}, {
  "country": "Italy",
  "Vodafone": 40,
  "Wind": 24,
  "Cosmote": 35
}, {
  "country": "France",
  "Vodafone": 22,
  "Wind": 9,
  "Cosmote": 9
}]

In den Werten, die aus einem verschachtelten Dataset bereitgestellt wurden, konnte ich jedem Objekt einen Schlüssel mit dem Namen "enabled" hinzufügen und das Dataset problemlos filtern, aber nicht herausfinden, wie ein Schlüssel an den Filterprozess angehängt wird.

edit3 Unbrauchbare Informationen aus der Frage entfernt:

Hier ist eine Arbeitsgeige: https://jsfiddle.net/fgseaxoy/2/


6
2018-03-30 09:32


Ursprung


Antworten:


Der Code von SergGr funktioniert gut, aber einige Teile können sauberer sein.

onclick

var fKeys = keys.slice();

//a helper object to record the state of keys 
var fKeyReference = fKeys.map(function () {
    return true; //used to indicate if the corresponding key is active
});

function getActiveKeys(reference) {
    return reference.map(function (state, index) {
        if (state) {
            return keys[index]; //just keep keys whoes state is true
        }
        return false; //return false to be filered
    }).filter(function (name) {
        return name
    });
}

...
.on('click', function (d) {
    if (fKeys.length === 1 && fKeys[0] === d) {
        return;
    }

    var index = keys.indexOf(d);
    fKeyReference[index] = !fKeyReference[index]; // toggle state of fKeyReference
    fKeys = getActiveKeys(fKeyReference);
    redraw();
});

.Stapel()

g.selectAll(".d3-group").remove();//remove all groups and draw them all again
stackedBars = g
    .selectAll(".d3-group")
    .data(d3.stack().keys(fKeys)(dataset));

aktualisiere die Achse (y.domain)

y.domain([
    0,
    1.2 * d3.max(dataset, function (d) {
        return fKeys.reduce(function (pre, key) {//calculate the sum of values of fKeys
            return pre + d[key];
        }, 0);
    })
]);

Und schlussendlich, jsfiddle


2
2018-04-03 13:57



Es gibt ein paar Dinge, die repariert werden mussten:

Zuerst weist JavaScript Objekte als Referenz zu. Es bedeutet, dass nach

var fKeys = keys;

beide fKeys und keys zeigen Sie auf das gleiche Array. Das ist nicht was du willst. Sie möchten etwas kopieren wie:

var fKeys = keys.slice();

Dann dein legendItem Der "Click" -Handler war falsch, weil er das ausgewählte Element nicht wirklich umschaltet. Was Sie wollen, ist etwas wie

        .on('click', function (keyToToggle) {
            // Go through both keys and fKeys to find out proper
            // position to insert keyToToggle if it is to be inserted
            var i, j;
            for (i = 0, j = 0; i < keys.length; i++) {
                // If we hit the end of fKeys, keyToToggle
                // should be last
                if (j >= fKeys.length) {
                    fKeys.push(keyToToggle);
                    break;
                }
                // if we found keyToToggle in fKeys - remove it
                if (fKeys[j] == keyToToggle) {
                    // remove it
                    fKeys.splice(j, 1);
                    break;
                }

                // we found keyToToggle in the original collection
                // AND it was not found at fKeys[j]. It means
                // it should be inserted to fKeys at position "j"
                if (keys[i] == keyToToggle) {
                    // add it
                    fKeys.splice(j, 0, keyToToggle);
                    break;
                }

                if (keys[i] == fKeys[j])
                    j++;
            }

            redraw();
        });

Als nächstes wollen Sie povide key Funktion wenn Sie anrufen data bekommen stackedBars. Dies ist wichtig, da ansonsten Daten durch den Index gebunden wären und immer das letzte Stück Daten entfernt würde.

    var stackedData = d3.stack().keys(fKeys)(dataset);
    var stackedBars = g
            .selectAll(".d3-group")
            .data(stackedData , function (__data__, i, group) {
                return __data__.key;
            });

Und schließlich, wenn Sie aktualisieren '.d3-rect' Du möchtest anrufen data noch einmal als untergeordnete Knoten zwischenspeichern Daten von der letzten Zeichnung und Sie möchten es mit neuen Daten überschreiben

        stackedBars.selectAll('.d3-rect')
                .data(function (d) {
                    return d; // force override with updated parent's data
                })
                .attr("x", function (d) {
                    return xz(d.data.country);
                })
                ...

Ohne diesen Aufruf würde das Verbergen des ersten Datenstücks ("Vodafone") andere gestapelte Teile nicht verschieben.

Auch gibt es ein paar zu viele globale Variablen (d. H. Zu wenige vars) und ein paar unnötige Variablen.

Aktualisieren (auto-skalieren y)

Wenn Sie möchten, dass Ihre Y-Skala aktualisiert wird, bewegen Sie sich var stackedData höher im Code der redraw so können Sie es verwenden, um Ihre zu berechnen y wie folgt

    var stackedData = d3.stack().keys(fKeys)(dataset);
    var autoScaleY = true; // scale Y according to selected data or always use original range
    var stackedDataForMax;
    if (autoScaleY && stackedData.length > 0) {
        // only selected data
        stackedDataForMax = stackedData;
    }
    else {
        // full range
        stackedDataForMax = d3.stack().keys(keys)(dataset);
    }
    var maxDataY = 1.2 * d3.max(stackedDataForMax.map(function (d) {
                return d3.max(d, function (innerD) {
                    return innerD[1];
                });
            }));
    y.domain([0, maxDataY]).rangeRound([height, 0]);

Sie können den ganzen Code in finden die Gabel deiner ursprünglichen Geige.


6
2018-04-02 00:57