Mike Bostock有一个很好的多系列线图here的例子,它使用紧凑的代码绘制了三个系列。他不是从原始数据中单独绘制每一行,而是创建一个新的数据对象(“城市”)进行迭代,然后一次性绘制所有三个路径元素:
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
[...等]但是假设我们想要动画这个图表,以便每个城市的线路一个接一个地“展开”?使用Bostock的数据结构是否可以这样做?
首先,我们创建一个补间函数来平滑地展开这些行(如this question中所示)。类似的东西:
function animateLine() {
var l = this.getTotalLength();
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) { return i(t); };
};
但是如果我们将此动画作为Mike Bostock的线条绘制代码的转换调用,那么它不会同时展开所有三行,而不是一个接一个地展开吗?
我可以看到一个hacky解决方案,它将定义三个独立的(但基本相同的)函数,根据一些轻微修改的原始数据,一次绘制一行一个...
var AustinLine = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.austin); });
var NewYorkLine = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.newyork); });
... [等]在三个独立的线条绘制函数中调用,调用我们的“animateLine”函数 -
function AustinPath () {
var line = svg.append("path")
.datum(data)
.attr("d", AustinLine)
.transition()
.duration(2000)
.ease("linear")
.attrTween("stroke-dasharray", animateLine);
... [对其他城市重复两次:NewYorkPath和SanFranciscoPath] ......
...然后终于以相应的延迟一个接一个地调用这些函数,逐个绘制线条。
setTimeout(Austinpath, 500);
setTimeout(NewYorkPath, 2500);
setTimeout(SanFranciscoPath, 4500);
... [等等]
这将逐一展开每一行,但代码中的重复是令人难以置信的,完全违背Mike Bostock原始例子的优雅。
我知道hacky解决方案很可怕......但是有更优雅的选择吗? Mike Bostock的原始“enter()。append”方法可以交错排列,依次绘制三条不同的行吗?
任何建议都非常感谢。提前谢谢。
答案 0 :(得分:0)
您为线路展开链接的问题使用了D3的转换框架,因此您不需要任何其他内容就可以让它们一个接一个地显示。这里的关键是.delay()
函数,它允许您指定转换应该开始的时间。它可以像D3中的其他所有东西一样,传递当前数据和索引而不是静态值的函数。
因此,知道展开需要一秒钟,我们可以像这样设置延迟:
.delay(function(d, i) { return i * 1000; })
即,将每个元素的转换延迟其索引时间1秒。在.transition()
之后添加此代码可以处理您想要的内容。
正如您所发现的那样,唯一的障碍是在消失和展开之前最初会绘制线条。这是因为您在开始转换之前将行的d
属性设置为其正确(结束)值。要避免此影响,只需将stroke-dasharray
属性设置为在转换开始时(animateLine(0)
)的值,然后再添加转换 - "0," + this.getTotalLength()
。完整演示此here。