d3.js带有可切换系列的堆叠杆

sco*_*ord 6 javascript arrays stackedbarseries d3.js

这次我试图创建一个带有可切换系列的堆叠条 - 基于Mike Bostock的例子(再次感谢Mike!)我已经成功地使它具有响应性和可缩放性,通过传奇的可切换系列是剩下的最后一件事.

我创建了图例项目,并使用键应用了正确的颜色:

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)
  });
Run Code Online (Sandbox Code Playgroud)

根据结构,为了创建可切换的项目,我得出的结论是,我必须能够从键和数据集切换它 - 或者还有另一种方法吗?我已设法从键中删除特定键,但不是从数据集中删除,我不知道如何正确映射它.

第二个问题是我无法找到切换密钥的方法,只是将其删除即可.这是原始数据集:

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
}]
Run Code Online (Sandbox Code Playgroud)

在从嵌套数据集提供的值中,我可以将名为"enabled"的键附加到每个对象,并且可以轻松地过滤数据集,但无法弄清楚如何附加键以帮助进行过滤过程.

edit3从问题中删除了无用的信息:

这是一个工作小提琴:https: //jsfiddle.net/fgseaxoy/2/

Ser*_*gGr 6

有一些事情需要修复:

首先,JavaScript通过引用分配对象.这意味着之后

var fKeys = keys;
Run Code Online (Sandbox Code Playgroud)

二者fKeyskeys指向相同的阵列.这不是你想要的.你想要一些复制,如:

var fKeys = keys.slice();
Run Code Online (Sandbox Code Playgroud)

然后你的legendItem"点击"处理程序是错误的,因为它并没有真正切换选定的项目.你想要的是什么

        .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();
        });
Run Code Online (Sandbox Code Playgroud)

接下来你要key打电话data来获取功能stackedBars.这很重要,因为否则数据将被索引绑定,并且总是会删除最后一段数据.

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

最后,当您更新时,'.d3-rect'您希望data再次调用,因为子节点缓存来自上一次绘制的数据,并且您希望使用新数据覆盖它

        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);
                })
                ...
Run Code Online (Sandbox Code Playgroud)

没有这样的呼叫,隐藏第一条数据("沃达丰")将不会移动其他堆叠的碎片.

还有一些太多的全球可用性(即太少的vars)和一些不必要的变量.

更新(自动缩放y)

如果您还希望更新Y尺度,则var stackedData在代码中向上移动,redraw以便您可以使用它来计算您y的跟随

    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]);
Run Code Online (Sandbox Code Playgroud)

您可以在原始小提琴的叉子中找到完整的代码.


bla*_*ool 2

SergGr 的代码运行良好,但某些部分可以更简洁。

单击时

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();
});
Run Code Online (Sandbox Code Playgroud)

。堆()

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

更新轴 ( 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);
    })
]);
Run Code Online (Sandbox Code Playgroud)

最后,jsfiddle