最近在做一个H5动画的小项目,由于项目周期充裕,前期主要是搭架构,也没用其他多余的动画库。由于经验不足,在搭架构的过程中也遇到挺多坑,比如webpack的配置问题、场景动画类的封装等等。这次就记录下如何异步控制动画的出现和消失。
回顾ES6的Promise和ES7的async/await
在ES6出来之前和不用其他动画库的情况下,很多人做简单动画基本都是用css3 animation + setTimeout + jQuery animate,但这样却面临一个问题:没错,回调地狱和嵌套层次太深,代码可读性大打折扣。
但现在有了ES6、ES7和babel,我们可以大胆地用promise和async/await来做动画的异步执行。
async函数是generator的一个语法糖,目前能被babel编译。它的优点在于外部不需要手动调用next方法,也不需要链式调用then方法,可以说是优于promise和generator。用法非常简单,来看一个简单例子:
const getUserData = url => (
new Promise((resolve, reject) => {
axios.get(url)
.then(({ data }) => resolve(data))
.catch(error => reject(error));
})
);
const getUserDataByAsync = async () => {
let _username;
try{
const { username } = await getUserData('https://github.com/username');
_username = username;
if(_username) {
/* do something */
}
}catch(err){
console.log(err);
}
};
getUserDataByAsync();
- 首先我们定义了一个getUserData函数,返回一个promise对象实例,用于抓取github上的用户信息。axios的get方法返回的也是一个promise对象,请求成功后会执行第一层promise的resolve方法,并把请求到的数据传出去。
- 接下来定义一个名为getUserDataByAsync的async函数(也可以这么声明:
async function getUserDataByAsync(){}
),用于处理请求成功后的操作。await关键字后调用getUserData,而在此之后的代码(在try之内,await之后)则会被阻塞,待请求成功后才会继续执行。await getUserData()的返回值则是getUserData函数里resolve后传入的data。这里我们用try-catch来捕获请求失败后的异常信息,catch打印出来的则是getUserData里请求失败后reject传入的error。 - 最后再调用getUserDataByAsync函数就行了。
我们用同样的方法封装一个动画延时器
- 首先我们先封装动画方法,这里为了操作节点方便一点用了jQuery
const animate = several => {
for (let effectName in several) {
let effect = `${effectName}`,
element_list = (several[effectName] instanceof Array) ? several[effectName] : [];
element_list.forEach(element => {
//为了防止display和animation冲突我们需要判断元素是否被隐藏了
if ($(element).is(':hidden')) {
$(element).show();
setTimeout(() => {
$(element).addClass(`animated ${effect}`);
}, 10);
} else {
$(element).addClass(`animated ${effect}`);
}
});
}
};
需配合自己写的css3 animation或引入animate.css库。
- 再封装一个延时器
const delay = (timeout = 0) => (
new Promise(resolve => {
setTimeout(resolve, timeout);
})
);
- 之后就可以随意控制动画的出现和消失了
const pageAnimationStart = async () => {
const _sceneWrap= $('#scene-wrap'),
_title = _scene_wrap.find('.title'),
_tree = _scene_wrap.find('.tree'),
_apples = _scene_wrap.find('.apple');
animate({
'scaleIn': [_sceneWrap]
});
await delay(1000);
animate({
'slideBounceInDown': [_tree]
});
await delay(1500);
animate({
'bounceIn': [_title, _apples]
});
};
window.addEventListener('load', function(){
pageAnimationStart();
}, false);
页面加载完成后,场景开始,先是整个场景背景_sceneWrap放大进入,1秒后大树_tree由上到下掉入,再过1.5秒后标题_title和苹果_apples跳入。
大概就是这样一个步奏,以异步执行、同步写法的方式,看起来特别舒服,清晰地展示了每一步该做什么,避免了的嵌套、回调和满屏的then方法。
这就是async/await在动画处理中的简单方法。其他应用场景等我研究过后再记录吧!
网友评论