我是D3.js的新手。我将Mike Bostock的this示例改编为this小提琴,其中提供了基于attrTween
和getPointAtLength
的逐点插值插值示例。
在原始示例中,引入了单个圆形,其遵循简单路径。在改编的示例中,不是引入单个圆,而是生成一束圆,其遵循更复杂的形状,即Inkscape中制作的玩具示例路径。
动画可以进行多次迭代,但过了一会儿,圆圈似乎陷入了循环,最终页面冻结了。但是,如果只生成一个圆圈,例如var RadiusData = [20];
(见下面的代码),动画保持循环正常。
可能导致这种情况的原因是什么?是否有一种简单的方法可以避免这种行为?
var w = $(window).width(),
h = $(window).height();
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + 0.25*w + "," + 0 + ")");
//some toy path data
var dInkScape ="m 360.1639,630.31678 c 1.0609,13.05167 -195.29107,-273.68628 -203.49722,-275.81173 -22.23818,-5.75983 -24.83733,-34.59299 -15.23811,-51.66666 17.17076,-30.54078 59.06286,-32.72422 85.71428,-14.04764 39.11203,27.40863 40.85844,83.86959 12.85717,119.7619 C 202.67874,456.39146 131.20349,457.65152 86.190506,420.21936 29.546262,373.1148 28.796105,286.43841 75.714265,232.36222 132.53844,166.8687 234.51201,166.64035 297.61902,223.07645 c 74.36943,66.50798 74.06939,183.83474 8.09531,255.95237 C 229.54464,562.29148 96.8291,561.45911 15.714334,485.93366 -76.453418,400.11684 -75.086213,251.98848 9.9999617,161.88605 105.45379,60.804734 269.012,62.70845 368.09519,157.36214 478.09632,262.44568 489.74023,530.06221 385.51394,638.12097 z";
var path = svg.append("svg:path")
.attr("d", dInkScape);
//some random data for the circle radii
var RadiusData = [20,50,25,5,40,22,50,66,72,23];
//introduce a circle for each element, set radius and give it some random color
var circle = svg.selectAll("circle")
.data(RadiusData).enter()
.append("svg:circle")
.attr("r", function(d){return d;})
.style("fill",function(d,i) {return "hsl(" + 120 + 100 *Math.random() + ",100%,25%)";})
.attr("transform", "translate(0," + -h / 3 + ")");
//with a 1 second delay introduce a new circle
function transition() {
circle.transition()
.duration(5000)
.delay(function(d,i){return 1000*i;})
.attrTween("transform", translateAlong(path.node()))
.each("end", transition);
}
transition();
// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
};
};
}
答案 0 :(得分:2)
问题是,只要任何一个圆圈完成转换,您就会在圆圈上的所有开始新的转换,如果您的初始选择有多个,则会导致重叠转换爆炸元件。
在选择中为每个元素调用transition.each回调。您可能想说d3.select(this)
为单个元素创建转换,如chained transitions example。
答案 1 :(得分:2)
问题是你在每个圈的移动结束时重新调用转换函数,但该函数为每个圈创建一个新的转换:
//with a 1 second delay introduce a new circle
function transition() {
circle.transition() //add a transition to every circle
.duration(5000)
.delay(function(d,i){return 1000*i;})
.attrTween("transform", translateAlong(path.node()))
.each("end", transition);
//re-run this function after *each* circle's transition completes
}
修复它的一个选项是让each
函数仅重新启动第一个元素的转换,即当i==0
或!i
为真时:
function transition() {
circle.transition()
.duration(5000)
.delay(function(d,i){return 1000*i;})
.attrTween("transform", translateAlong(path.node()))
.each("end", function(d,i){if (!i) transition(); });
}
正如@mbostock刚刚建议的那样,另一个选项是让你的函数只适用于单个元素:
function transitionThis(d,i) { //index is given to the function
d3.select(this).transition()
.duration(5000)
.delay(1000*i) //not a function anymore
.attrTween("transform", translateAlong(path.node()))
.each("end", transitionThis); //repeat for this element
}
circle.each(transitionThis); //start transitions for each
或者,如果您只想应用延迟一次,错开开始时间,然后让所有圆圈均匀移动而不停在路径的开头:
function transitionThis(d,i) {
d3.select(this).transition()
.duration(5000) //no delay once started
.ease("linear") //move steadily at all points on path
.attrTween("transform", translateAlong(path.node()))
.each("end", transitionThis); //repeat for this element
}
circle.transition().duration(0)
.delay(function(d,i){return 1000*i;}) //stagger starts
.each(transitionThis); //start transitions for each
另一件事:至少在调试过程中,以编码方式停止任何无限循环总是一个好主意。我在上面的小提琴中通过向整个svg添加一个点击功能来创建一个新的圆圈过渡,中断无限循环版本:
svg.on("click", function() { //Stop the infinite transitions!
circle.transition(); //create a new empty transition
});