以下是两个jsfiddle:
调用JSFiddle 1中的tick的代码:
path.transition()
.duration(duration)
.ease("linear")
.attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
.each("end", tick);
调用JSFiddle 2中的tick的代码:
axis.transition()
.duration(duration)
.ease("linear")
.call(xAxis)
.each("end", tick);
做一个测试1:
- 使用IE 11或Chrome浏览器打开JSFiddle 1和JSFiddle2。
- 观察动画svg行
做一个测试2:
- 使用IE9浏览器打开JSFiddle2和jsfiddle1。
- 观察动画svg行
在测试1中,我观察到动画svg线的起点随着轴的移动而移动,因此它在视觉上看起来是同步的。因此,您将看到一条平滑的动画线。
在测试2中,我观察到svg线的起点没有随着轴的移动而移动,因此从视觉上看它是不同步的。因此,您会看到一条有点奇怪的抖动线。
有没有人知道是什么原因导致这种情况发生?如何在IE9中解决这个问题?
我上传了一个视频来演示这个问题:视频1
编辑
根据Amelia的回答,我上传了custom tween function
: video 2的改进版本
我对她的回答做了一些小的调整,并使用x(t - n + 2)而不是x(t - n + 1)。尽管这条线不光滑,但至少它现在与轴位移同步:
我怀疑问题仅仅是IE9不支持requestAnimationFrame()
方法。D3使用requestAnimationFrame(如果可用)来同步转场。
这是有区别的,因为requestAnimationFrame方法将一系列连续的调用捆绑到一个"帧"中——并向它们传递相同的时间戳值。如果没有requestAnimationFrame方法,每个转换将获得一个不同的时间戳,延迟时间取决于浏览器完成前一个任务所花费的时间。
不幸的是,对于坚持使用过时浏览器的人来说,没有简单的解决方案可以解决这个问题。
你可以做的是尽量使你的代码高效。例如,当前您正在创建单独的转换,以相同的转换量来移动每个路径。如果你创建了一个<g>
元素并在其中绘制了所有的路径,那么你只需要一个变换和一个过渡。
var graph = g.append("svg")
.attr("width", width)
.attr("height", height + margin.top + margin.bottom);
var plot = graph.append("g").attr("class", "plot");
var path1 = plot.append("g").append("path")
/* etc for the other paths */
然后在tick()
函数中:
当你重画线条时,不要在路径元素上设置任何变换,而是用
清除plot变换plot.attr("transform", null);
用
替换所有路径转换plot.transition() .duration(duration) .ease("linear") .attr("transform", "translate(" + x(t - n + 1) + ")");
(让我知道这在IE9中是否有明显的改进——IE11仿真工具并没有真正显示这个问题,所以我无法有效地测试。)
顺便说一下,你也可以通过使用选择&数据连接来绘制不同的线。在d3中查看更多关于选择和连接的教程。
编辑
感谢您的反馈,很抱歉它仍然没有做出改进。下面的方法更复杂,如果您是d3新手,可能很难理解,但它应该强制同步。需要注意的是,动画可能仍然是跳跃的,但是线和轴应该同时跳跃!
我们的想法是只使用一个转场,然后在转场的每个刻度更新情节转换和轴。这是使用自定义的"tween"函数完成的。它假设你已经做了上面的修改。特别地,我不允许默认的轴函数为每个子组件创建单独的过渡,而是使用与线条相同的方法:绘制不带过渡的轴,然后使用转换将其侧向移动。这两个转换都在同一个渐变函数中应用。
plot.attr("transform", null);
// redraw the axis, without transition
axis.call(xAxis).attr("transform", null);
// slide the line left
plot.transition()
.duration(duration)
.ease("linear")
.attrTween("transform",
function(){
//this function is run for every element
//in the selection (which only has one element).
//create an interpolator function
var inter = d3.interpolateString("translate(0,0)",
"translate(" + x(t - n + 1) + ")");
return function(t) {
//this function is called at every "tick"
//of the transition
//transition the axis
axis.attr("transform", inter(t) );
//return the value to transition the plot
return inter(t);
};
})
.each("end", tick); //loop
有一个额外的复杂性:x轴<g>
元素已经有一个transform属性,将它移动到图形的底部。我每次都可以重复这个转换,这样转换后的属性看起来就像axis.attr("transform", "translate(0," + height + ")" + inter(t) );
。然而,我决定通过使用嵌套的<g>
元素作为轴来保持简单:垂直转换应用于外部组,水平转换应用于内部组:
var xAxis = d3.svg.axis().scale(x)
.orient("bottom")
.outerTickSize(0);
var axis = graph.append("g") //outer group handles the vertical transform
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.append("g")
//this nested <g> will be stored in the `axis` variable!
//and therefore will get the horizontal transform attribute
.call(x.axis = xAxis);
注意,我还将.outerTickSize(0)
添加到轴定义中。这可以防止轴在域的开始和结束处绘制额外的标记-外部标记在Y轴的底部出现奇怪的闪烁线,每次重新绘制轴时出现,然后在轴移动时立即消失。
注:如果你的客户仍然感到失望,冷静下来。礼貌地提醒他或她IE9是一个过时的浏览器,没有最新浏览器生成流畅动画的能力。如果有人想要最新的&最伟大的网站,他们需要使用最新的&最大的浏览器。