最近在做一個H5動畫的小項目,由于項目周期充裕,前期主要是搭架構,也沒用其他多余的動畫庫。由于經驗不足,在搭架構的過程中也遇到挺多坑,比如webpack的配置問題、場景動畫類的封裝等等。這次就記錄下如何異步控制動畫的出現(xiàn)和消失。
回顧ES6的Promise和ES7的async/await
在ES6出來之前和不用其他動畫庫的情況下,很多人做簡單動畫基本都是用css3 animation + setTimeout + jQuery animate,但這樣卻面臨一個問題:沒錯,回調地獄和嵌套層次太深,代碼可讀性大打折扣。
但現(xiàn)在有了ES6、ES7和babel,我們可以大膽地用promise和async/await來做動畫的異步執(zhí)行。
async函數(shù)是generator的一個語法糖,目前能被babel編譯。它的優(yōu)點在于外部不需要手動調用next方法,也不需要鏈式調用then方法,可以說是優(yōu)于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函數(shù),返回一個promise對象實例,用于抓取github上的用戶信息。axios的get方法返回的也是一個promise對象,請求成功后會執(zhí)行第一層promise的resolve方法,并把請求到的數(shù)據傳出去。
- 接下來定義一個名為getUserDataByAsync的async函數(shù)(也可以這么聲明:
async function getUserDataByAsync(){}),用于處理請求成功后的操作。await關鍵字后調用getUserData,而在此之后的代碼(在try之內,await之后)則會被阻塞,待請求成功后才會繼續(xù)執(zhí)行。await getUserData()的返回值則是getUserData函數(shù)里resolve后傳入的data。這里我們用try-catch來捕獲請求失敗后的異常信息,catch打印出來的則是getUserData里請求失敗后reject傳入的error。 - 最后再調用getUserDataByAsync函數(shù)就行了。
我們用同樣的方法封裝一個動畫延時器
- 首先我們先封裝動畫方法,這里為了操作節(jié)點方便一點用了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);
})
);
- 之后就可以隨意控制動畫的出現(xiàn)和消失了
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跳入。
大概就是這樣一個步奏,以異步執(zhí)行、同步寫法的方式,看起來特別舒服,清晰地展示了每一步該做什么,避免了的嵌套、回調和滿屏的then方法。
這就是async/await在動畫處理中的簡單方法。其他應用場景等我研究過后再記錄吧!