d3.js attrTween的意外行为

d3.js attrTween的意外行为,d3.js,D3.js,我是D3.js的新手。我将Mike Bostock的示例改编为fiddle,它提供了一个基于attrTween和getPointAtLength的沿路径点插值示例 在原始示例中,引入了一个简单路径下的单圆形状。在调整后的示例中,不是引入单个圆,而是生成一组圆,这些圆遵循更复杂的形状,即Inkscape中制作的玩具示例路径 动画在多次迭代中运行良好,但过了一会儿,圆圈似乎陷入循环,最终页面冻结。但是,如果仅生成一个圆,例如var RadiuData=[20];请参见下面的代码,动画保持循环状态 这

我是D3.js的新手。我将Mike Bostock的示例改编为fiddle,它提供了一个基于attrTween和getPointAtLength的沿路径点插值示例

在原始示例中,引入了一个简单路径下的单圆形状。在调整后的示例中,不是引入单个圆,而是生成一组圆,这些圆遵循更复杂的形状,即Inkscape中制作的玩具示例路径

动画在多次迭代中运行良好,但过了一会儿,圆圈似乎陷入循环,最终页面冻结。但是,如果仅生成一个圆,例如var RadiuData=[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 + ")";
    };
  };
}

问题是,每当任何一个圆完成过渡时,都会在圆上的所有圆上开始新的过渡,如果初始选择包含多个元素,则会导致重叠过渡的爆炸


将为选择中的每个元素调用transition.each回调。您可能想说d3。选择此选项可为单个元素创建转换,如。

问题是,每当任何一个圆完成转换时,您都会在圆上的所有圆上启动新转换,如果您的初始选择包含多个元素,则会导致重叠转换的爆发


将为选择中的每个元素调用transition.each回调。您可能想说d3。选择此选项可为单个元素创建过渡,如中所示。

问题是您在每个圆的移动结束时重新调用过渡函数,但该函数会为每个圆创建新的过渡:

修复它的一个选项是让each函数只重新启动第一个元素的转换,即当i==0或!我是真的:

另一个选项,正如刚才@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
}); 

问题在于,您在每个圆的移动结束时重新调用了transition函数,但该函数会为每个圆创建一个新的transition:

修复它的一个选项是让each函数只重新启动第一个元素的转换,即当i==0或!我是真的:

另一个选项,正如刚才@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
}); 

我在.duration5000之后插入了以下行:.stylefill,functiond,I{console.logct:+++ct;console.logi;return red;}。我在里面也做了同样的记录延迟。。。但这是一种更快的数据收集方法。这里是它出现的情况:在每次迭代中,记录的索引数量加倍,即第一次迭代日志1,2,第二次日志3,4,5,6…等等。因此,队列将被填满,并最终导致页面无响应。星期五,漫长工作周的结束…我肯定我错过了显而易见的,但现在把这记为有趣的…现在休息一下。忘了提到我将圆圈的数量减少到了两个,以便于测试…原因是1,2…3,4,5,6…等等。我在.duration5000之后插入了以下行:。stylefill,functiond,I{console.logct:+++ct;console.logi;返回红色;}。我在内部做了相同的日志记录。延迟…但这是一种更快的收集数据的方法。结果如下:在每次迭代中,记录的索引数量加倍,即第一次迭代日志1,2,第二次日志3,4,5,6…等等。因此,队列填满,最终导致页面无响应。周五,漫长工作周结束…我肯定我忽略了显而易见的,但现在把它记为有趣的…现在休息一下。忘了提到我将圆圈的数量减少到了两个,以使测试更容易…原因是1,2…3,4,5,6…等等。感谢所有的例子,现在更清楚了!关于停止功能的观点很好。我已经有点担心了
我认为运行我的示例会很烦人,因为我的代码也会使您的浏览器崩溃。我会确保下一次我会包括你的建议。是的,那有点烦人,但基于你的问题,我期待它。谢天谢地,Chrome的单独标签作为单独的线程结构。我必须关闭选项卡,重新加载,然后在错误脚本耗尽我所有的CPU之前,在代码中引入语法错误,然后按run,这样它就会出错,我就有时间编辑了!我真希望JSFIDLE可以选择从主窗口中删除结果帧——以前我因为运行无限脚本错误而丢失了很多工作,不得不删除整个页面。感谢所有示例,现在更清楚了!关于停止功能的观点很好。我已经有点担心运行我的示例会很烦人,因为我的代码也会使您的浏览器崩溃。我会确保下一次我会包括你的建议。是的,那有点烦人,但基于你的问题,我期待它。谢天谢地,Chrome的单独标签作为单独的线程结构。我必须关闭选项卡,重新加载,然后在错误脚本耗尽我所有的CPU之前,在代码中引入语法错误,然后按run,这样它就会出错,我就有时间编辑了!我真希望JSFIDLE可以选择从主窗口终止结果帧——以前我因为运行一个无限脚本错误而不得不终止整个页面而损失了很多工作。