JS:如何同步多个动画的时间?

问题描述

当我动态添加一个使用 JS 制作动画的项目时,如何让它们在同一时间轴上保持同步,如下所示:http://youtube.com/watch?v=AGiTmjFHs8M&t=9m23s?我看到一个教程,展示了使用 JS 动画与 CSS 的好处是它们继承了相同的时间线。

<div class="body"></div>
<button onclick="addItem()">Add</button>

function addItem() {
   let body = document.querySelector('.body');
   let newEl = document.createElement('div');
   newEl.innerText = "I am a new Item";
   newEl.animate([
      { 
          transform: 'translate(0px,0px)',transform: 'translate(500px,500px)',}
      ],{
         duration: 2000,iterations: Infinity,});

   body.appendChild(newEl);
}

解决方法

如果您所有的 Animation 对象确实共享相同的 duration 并且您希望它们同时开始和结束,您只需设置新 iterationStart EffectTiming >Animation 对象到已经运行的对象的 ComputedTiming .progress 值。
但是请注意,您必须等待新的 Animation 对象准备好才能获得前一个的计算值,否则您将延迟一帧。为此,您只需等待 animation.ready 承诺。

要获取之前的Animation对象,可以在创建时通过Element.animate()存储,也可以通过document.getAnimations()访问文档上当前运行的Animations集合,从

let i = 0;
async function addItem() {

  i++;
  const body = document.querySelector(".body");
  const newEl = document.createElement("div");
  newEl.classList.add("item");
  newEl.textContent = "I am a new Item " + i;

  // get a previous Animation if any
  const previous_animation = document.getAnimations()[0];

  // create the new Animation object
  // slightly offset on the x axis only
  // to check if they are indeed in sync
  const anim = newEl.animate([{
    transform: "translate(" + (i * 10) + "px,0px)",transform: "translate(" + (i * 10 + 250) + "px,250px)",}],{
    duration: 2000,iterations: Infinity
  });
  // when it's ready to start
  await anim.ready;

  // get the current progress of the first Animation object
  const computed_timing = previous_animation?.effect.getComputedTiming();
  if( computed_timing ) {
    // update our new Animation object to the same progress value
    anim.effect.updateTiming( {
      iterationStart: computed_timing.progress
    });
  }
  
  body.appendChild(newEl);

}
.item {
  position: absolute;
}
<div class="body"></div>
<button onclick="addItem()">Add</button>


请注意,正如用户 brianskoldcomment below 中指出的那样,动画的 startTime property 可以设置为更早的时间。这样做会使动画确实在此时开始。

所以为了同步两个动画,这可能是最好的方法:

let i = 0;
function addItem() {

  i++;
  const body = document.querySelector(".body");
  const newEl = document.createElement("div");
  newEl.classList.add("item");
  newEl.textContent = "I am a new Item " + i;

  // get a previous Animation if any
  const previous_animation = document.getAnimations()[0];

  // create the new Animation object
  // slightly offset on the x axis only
  // to check if they are indeed in sync
  const anim = newEl.animate([{
    transform: "translate(" + (i * 10) + "px,iterations: Infinity
  });

  if( previous_animation ) {
    // set the startTime to the same
    // as the previously running's one
    // note this also forces anim.ready to resolve directly
    anim.startTime = previous_animation.startTime;
  }

  body.appendChild(newEl);

}
.item {
  position: absolute;
}
<div class="body"></div>
<button onclick="addItem()">Add</button>