You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让多个D3.js过渡动画以恒定速率重复运行?

解决D3.js波浪动画从连贯滚动变成同步弹跳的问题

嘿,我明白你的问题了!一开始你的矩形能形成流畅的波浪滚动效果,但没过多久就变成了上下同步弹跳的状态,这是因为你的动画循环逻辑在第一次运行后丢失了矩形之间的延迟偏移。

问题根源

看你的代码,repeat函数里的delay依赖于start参数:第一次调用repeat(true)时,每个矩形会用d.delay(也就是i*20)作为延迟,依次启动动画,形成波浪效果。但当动画结束触发on('end', repeat)时,repeat没有传入参数,start变成了undefined,此时delay被设为0,所有矩形同时开始下一轮动画,自然就变成同步弹跳了。

修复方案

我们只需要让初始的延迟偏移只在第一次启动时生效,之后的循环让每个矩形按自己的节奏重复动画,就能保持波浪效果。这里有两种简单的修改方式:

方法一:用数据标记第一次运行

在给矩形绑定数据时添加一个firstRun标志,用来控制是否应用初始延迟:

for (let i = 0; i < numRects; i++) { 
  const height = scaleY(seaLevel) 
  const bar = svg.append('rect') 
    .attr('fill', 'blue') 
    .attr('opacity', 0.7) 
    .attr('x', i * barWidth + xMargin) 
    .attr('width', barWidth) 
    .datum({delay: i * 20, firstRun: true}) // 添加firstRun标记

  // 修改repeat函数
  function repeat() { 
    const low = scaleY(seaLevel - height) 
    const high = scaleY(seaLevel + height) 
    // 第一次运行用初始delay,之后用0
    const currentDelay = bar.datum().firstRun ? bar.datum().delay : 0;
    // 第一次运行后重置标记
    bar.datum().firstRun = false;

    bar 
      .attr('y', waveBoxHeight - low) 
      .attr('height', low) 
      .transition() 
      .ease(d3.easeQuad) 
      .delay(currentDelay) 
      .duration(1000) 
      .attr('y', waveBoxHeight - high) 
      .attr('height', high) 
      .transition() 
      .ease(d3.easeQuad) 
      .duration(1000) 
      .attr('y', waveBoxHeight - low) 
      .attr('height', low) 
      .on('end', repeat) 
  } 
  repeat() 
}

方法二:分离初始动画和循环逻辑

把带延迟的初始动画和不带延迟的循环逻辑分开,更清晰直观:

for (let i = 0; i < numRects; i++) { 
  const height = scaleY(seaLevel) 
  const low = scaleY(seaLevel - height) 
  const high = scaleY(seaLevel + height) 

  const bar = svg.append('rect') 
    .attr('fill', 'blue') 
    .attr('opacity', 0.7) 
    .attr('x', i * barWidth + xMargin) 
    .attr('width', barWidth) 
    .attr('y', waveBoxHeight - low) 
    .attr('height', low)
    .datum({delay: i * 20}) 

  // 初始带延迟的动画
  bar.transition()
    .ease(d3.easeQuad)
    .delay(d => d.delay)
    .duration(1000)
    .attr('y', waveBoxHeight - high)
    .attr('height', high)
    .transition()
    .ease(d3.easeQuad)
    .duration(1000)
    .attr('y', waveBoxHeight - low)
    .attr('height', low)
    .on('end', repeat)

  // 循环动画,不带额外延迟
  function repeat() {
    bar.transition()
      .ease(d3.easeQuad)
      .duration(1000)
      .attr('y', waveBoxHeight - high)
      .attr('height', high)
      .transition()
      .ease(d3.easeQuad)
      .duration(1000)
      .attr('y', waveBoxHeight - low)
      .attr('height', low)
      .on('end', repeat)
  }
}

效果说明

修改后,第一次启动时每个矩形会按i*20的延迟依次开始动画,形成流畅的波浪滚动。之后的循环中,每个矩形会在自己的动画结束后立即开始下一轮,保持彼此之间的时间偏移,再也不会变成同步弹跳的状态啦!

内容的提问来源于stack exchange,提问作者Teachrdan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:53:55