在D3中,如何在灰色和初始颜色之间切换正方形的颜色?

时间:2013-11-26 23:56:36

标签: svg d3.js

我正在学习d3并且正在编写一个可视化,需要在初始颜色和灰色之间切换形状。另一个例子并不能完全满足我的需要(d3 javascript alternate colors on click),因为我希望这两种颜色是“初始”和“灰色”而不是两种硬编码颜色。

我高兴地注意到我的矩形有两种相似的颜色,看起来像原始和当前的颜色:

我在下方的尝试让您可以将形状单击为灰色,但它们不会单击回原始颜色。你能告诉我我做错了什么吗?我也愿意完全修改方法。

谢谢!

        var w = 300;
        var h = 200;

        var colors = ["Red","Green","Blue"];

        var svg = d3.select("body")
            .append("svg")
            .attr("width", w)
            .attr("height",h);

        var squares = svg.selectAll("rect")
           .data(colors)
           .enter()
           .append("rect")
           .on("click", function(){
                if(d3.select(this).style("fill") !=  "Gray"){
                    d3.select(this).style("fill", "Gray")
                }
                else{
                    d3.select(this).style("fill", d3.select(this).fill)
                };
            });

        squares.attr("x", function(d, i){
                return 100+i*80;
                })
           .attr("y", 100)
           .attr("width", 40)
           .attr("height", 40)
           .attr("fill", function(d){
                return d;
            });;

1 个答案:

答案 0 :(得分:3)

您正在采用的方法读取对象的fill样式属性的当前值,以决定如何对点击作出反应。有人可能会说你“在DOM中存储真相”,这并不理想。例如,如果您console.log(d3.select(this).style("fill"))预期"Gray",则最有可能获得#808080

相反,您可以将rect的状态存储在其基准面上。在点击处理程序中,可以使用d3.select(this).datum()获取数据。然后你可以说:

var clicked = d3.select(this);
var datum = clicked.datum();
if(!datum.isToggled) { datum.isToggled = true; }
else { datum.isToggled = false; }

这样,您将在绑定到该对象的基准上存储状态(切换或不切换)。此时,您可以根据clicked为true来设置datum.isToggled的颜色。在你的情况下:

if(datum.isToggled) { clicked.style("fill", "Gray"); }
else { clicked.style("fill", datum); }

在您设置填充的代码中,您有一个错误,因为您将填充设置为d3.select(this).fill未定义。在您的情况下,您想要的填充是分配给datum的值,因为您将节点绑定到字符串数组。

顺便提一下,请注意,由于您绑定了一个字符串数组,datum.isToggled最终解析为"Red".isToggled,这在JS中令人惊讶地起作用,但看起来很怪异,并且出乎意料。要解决它,你想要绑定到一个对象数组,如:

[
  { fill: "Red" },
  { fill: "Green" },
  { fill: "Blue" }
]

然后,当您要应用填充时,使用datum.fill

.attr("fill", function(d){
  return d.fill;
});

最后,请注意您在一个地方使用.attr("fill", ...)和另一个.style("fill", ...)的情况?这是有风险的,因为一个优先于另一个。相反,只使用这两者中的一个。

补充说明:

我在上面引用的数据是由于在d3选择上调用.data(...)而绑定到DOM元素的东西。在您的情况下,您使用字符串数组调用data(colors),因此3个结果DOM元素中的每一个将最终具有这3个字符串之一的数据("Red",{{1 }或"Green")。在您的代码中,正如您目前所拥有的那样,如果单击Red DOM元素,则从单击处理程序内部运行"Blue"将返回字符串d3.select(this).datum()。这就是d3中DOM元素与基准相关联(或绑定)的含义。

我的一个建议是绑定到一个通用JavaScript对象数组,而不是一个字符串数组。您可以将每个对象视为属性的容器。在您的情况下,每个对象只有1个属性"Red"。因此,如果您这样做,调用fill将返回javascript对象clicked.datum()(或绿色或蓝色)。

{ fill: "Red" }并非与d3内置或完全相关(如果您愿意,可以将其称为isToggled)。它只是你在基准面上设置的属性。像一个标记。即在最后一段中的“红色”数据上,运行foo会产生datum.isToggled = true。下次与此红色关联的DOM元素被点击时,{ fill: "Red", isToggled: true }将返回该同一个对象,.datum()设置为isToggled。这就是我的意思是让数据表示DOM元素的状态。然后,您的代码可以决定将true设置为isToggled,产生false,然后相应地设置DOM元素的颜色。

我还指出,由于您的代码目前使用的是字符串数组 - 而不是{ fill: "Red", isToggled: false }的数组 - 如果您保持这种方式,可能会将{}指定为任何这些字符串的属性都不起作用。但事实上,由于JavaScript的特性,这将起作用。但我觉得这里解释得太多了......