问题描述
当我动态添加一个使用 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>
请注意,正如用户 brianskold 在 comment 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>