如何使用Promise链接CSS动画



我正在努力弄清楚Promise是如何工作的,并制作一个又一个CSS动画。但它们同时运行。。。

JS fiddle

let move_boxes = () => {
return new Promise( (resolve, reject) => {
resolve ('boxes moved!')
})
};
let move_box_one = () => {
return new Promise( (resolve, reject) => {
resolve (document.getElementById('div_two').style.animation = 'move 3s forwards')
console.log('box one moved!')   
})
}
let move_box_two = () => {
return new Promise( (resolve, reject) => {
resolve (document.getElementById('div_one').style.animation = 'move 3s forwards')
console.log('box two moved!')       
})
}
move_boxes().then(() => {
return move_box_one();
}).then(() => {
return move_box_two();
});

与发布的答案相反,我不建议使用window.setTimeout,因为它不能保证计时器与动画结束事件同步,这些事件有时会被卸载到GPU。如果您想更加确定,您应该监听animationend事件,并在回调中解决它,即:

let move_box_one = () => {
return new Promise((resolve, reject) => {
const el = document.getElementById('div_one');
const onAnimationEndCb = () => {
el.removeEventListener('animationend', onAnimationEndCb);
resolve();
}
el.addEventListener('animationend', onAnimationEndCb)
el.style.animation = 'move 3s forwards';
});
}

更好的是,由于您正在为这两个框编写一些重复的逻辑,您可以将所有这些抽象为一个返回promise的泛型函数:

// We can declare a generic helper method for one-time animationend listening
let onceAnimationEnd = (el, animation) => {
return new Promise(resolve => {
const onAnimationEndCb = () => {
el.removeEventListener('animationend', onAnimationEndCb);
resolve();
}
el.addEventListener('animationend', onAnimationEndCb)
el.style.animation = animation;
});
}
let move_box_one = async () => {
const el = document.getElementById('div_one');
await onceAnimationEnd(el, 'move 3s forwards');
}
let move_box_two = async () => {
const el = document.getElementById('div_two');
await onceAnimationEnd(el, 'move 3s forwards');
}

此外,您的move_boxes函数有点复杂。如果您想异步运行单独的盒子移动动画,请将其声明为异步方法,并简单地等待单独的盒子运动函数调用,即:

let move_boxes = async () => {
await move_box_one();
await move_box_two();
}
move_boxes().then(() => console.log('boxes moved'));

参见概念验证示例(或者你可以从这个JSFiddle中看到它,我从你原来的例子中派生了它(:

// We can declare a generic helper method for one-time animationend listening
let onceAnimationEnd = (el, animation) => {
return new Promise(resolve => {
const onAnimationEndCb = () => {
el.removeEventListener('animationend', onAnimationEndCb);
resolve();
}
el.addEventListener('animationend', onAnimationEndCb)
el.style.animation = animation;
});
}
let move_box_one = async () => {
const el = document.getElementById('div_one');
await onceAnimationEnd(el, 'move 3s forwards');
}
let move_box_two = async () => {
const el = document.getElementById('div_two');
await onceAnimationEnd(el, 'move 3s forwards');
}
let move_boxes = async () => {
await move_box_one();
await move_box_two();
}
move_boxes().then(() => console.log('boxes moved'));
#div_one {
width: 50px;
height: 50px;
border: 10px solid red;
}
#div_two {
width: 50px;
height: 50px;
border: 10px solid blue;
}
@keyframes move {
100% {
transform: translateX(300px);
}
}
@keyframes down {
100% {
transform: translateY(300px);
}
}
<div id="div_one"></div>
<div id="div_two"></div>

只有当您知道相应的动画事件已完成时,才需要解析promise。

您可以使用animationend事件,这比使用预期持续时间的setTimeout更合适。

创建一个实用程序函数来创建一个承诺也是有用的,然后将其用于任何元素和任何动画描述:

const promiseAnimation = (elem, animation) => new Promise(resolve => {
elem.style.animation = animation;
elem.addEventListener("animationend", resolve);
});
const $ = document.querySelector.bind(document);
const moveBoxOne = () => promiseAnimation($("#div_one"), "move 3s forwards");
const moveBoxTwo = () => promiseAnimation($("#div_two"), "move 3s forwards");
Promise.resolve()
.then(moveBoxOne)
.then(() => console.log("one has moved"))
.then(moveBoxTwo)
.then(() => console.log("two has moved"))
div { display: inline-block; position: relative; border: 1px solid; }
@keyframes move {
from {margin-left: 0px;}
to {margin-left: 100px;}
}
<div id="div_one">div_one</div><br>
<div id="div_two">div_two</div>

您需要等待动画完成,下面是一个工作代码,

let move_boxes = () => {
return new Promise( (resolve, reject) => {
resolve ('boxes moved!')
})
};
let move_box_one = () => {
return new Promise( (resolve, reject) => {
document.getElementById('div_two').style.animation = 'move 3s forwards';
setTimeout(()=> resolve(), 3000);   
})
}
let move_box_two = () => {
return new Promise( (resolve, reject) => {
document.getElementById('div_one').style.animation = 'move 3s forwards';
setTimeout(()=> resolve(), 3000);
})
}
move_boxes().then(() => {
return move_box_one();
}).then(() => {
return move_box_two();
});

工作小提琴:https://jsfiddle.net/bu96cxak/

Promise并不意味着它将是异步的。由于解析是即时的,所以它们很可能在同一时间运行。如果您想延迟,您可以使用setTimeout在调用中创建延迟。

const delay = (dl) => new Promise(r => {
setTimeout(r, dl)
})
let move_box_one = () => {
document.getElementById('div_two').style.animation = 'move 3s forwards'
console.log('box one moved!') 
}
let move_box_two = () => {
document.getElementById('div_one').style.animation = 'move 3s forwards'
console.log('box two moved!')       
}
(async function() {
move_box_one()
await delay(200)
move_box_two()
})()

最新更新