對(duì)于前端來說,我感覺最大的魅力就是在于動(dòng)畫效果了既鞠;最初入坑前端就是想做出炫酷的動(dòng)畫,其實(shí)可以學(xué)習(xí)AE什么的盖文,當(dāng)時(shí)不懂嘱蛋,就入了前端程序員的坑。當(dāng)然用代碼實(shí)現(xiàn)出炫酷的動(dòng)畫效果,更會(huì)讓自己充滿成就感洒敏。
1. 動(dòng)畫簡(jiǎn)介
在一段時(shí)間內(nèi)龄恋,快速地多次改變UI外觀,由于人眼會(huì)產(chǎn)生視覺殘留桐玻,所以最后會(huì)看到連續(xù)的動(dòng)畫篙挽。對(duì)于人眼來說荆萤,動(dòng)畫幀率超過16FPS镊靴,就比較流暢了,超過32FPS就會(huì)非常的細(xì)膩平滑链韭,而超過32FPS偏竟,人眼基本上就感受不到差別了。由于動(dòng)畫的每一幀都是要改變UI輸出敞峭,所以在一個(gè)時(shí)間段內(nèi)連續(xù)的改變UI輸出是比較耗資源的踊谋,對(duì)設(shè)備的軟硬件系統(tǒng)要求都較高,所以在UI系統(tǒng)中旋讹,動(dòng)畫的平均幀率是重要的性能指標(biāo)殖蚕,而在Flutter中,理想情況下是可以實(shí)現(xiàn)60FPS的沉迹,這和原生應(yīng)用能達(dá)到的幀率是基本是持平的睦疫。—— 《flutter實(shí)戰(zhàn).動(dòng)畫簡(jiǎn)介》
2. flutter動(dòng)畫
在web前端鞭呕,我們實(shí)現(xiàn)動(dòng)畫可以通過css過渡元素蛤育,定義css關(guān)鍵幀,js等定義動(dòng)畫函數(shù)等方式去實(shí)現(xiàn)葫松,那么flutter如何去實(shí)現(xiàn)動(dòng)畫效果呢瓦糕?如果是想簡(jiǎn)單的實(shí)現(xiàn)動(dòng)畫,可以選擇flutter內(nèi)置的一些動(dòng)畫Widget腋么,flutter已經(jīng)封裝好了:AnimatedPadding
咕娄,AnimatedPositioned
,AnimatedOpacity
珊擂,AnimatedAlign
圣勒,AnimatedContainer
,AnimatedDefaultTextStyle
未玻,這些拿來即用灾而,但是這些不能隨心控制;想要自己自定義實(shí)現(xiàn)動(dòng)畫的話那就需要以下的幾個(gè)步驟:
- 創(chuàng)建一個(gè)動(dòng)畫控制器
AnimationController
- 添加一個(gè)描述動(dòng)畫的過程
Curve
扳剿,返回的是一個(gè)Animation - 添加
tween
,控制動(dòng)畫的執(zhí)行范圍旁趟,需要執(zhí)行animate方法,返回Animation
AnimationController
: 創(chuàng)建動(dòng)畫控制器,默認(rèn)動(dòng)畫執(zhí)行范圍是[0,1]锡搜,如果需要改變的話橙困,需要用到tween
3. 開始制作簡(jiǎn)單的動(dòng)畫
動(dòng)畫效果:一個(gè)正方形,先變大耕餐,然后變顏色凡傅,然后再變小,還原顏色
前提準(zhǔn)備肠缔,新建好項(xiàng)目夏跷,新建class AnimationDemo
class AnimationDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return AnimationDemoState();
}
}
class AnimationDemostate extends State<AnimationDemo> with SingleTickerProviderStateMixin {
// SingleTickerProviderStateMixin 性能有關(guān),需要執(zhí)行Ticker
// 動(dòng)畫控制器需要傳這個(gè)參數(shù)明未,效果是在頁面不可見的區(qū)域槽华,動(dòng)畫不會(huì)執(zhí)行,避免消耗資源趟妥。
}
3.1 定義動(dòng)畫和控制器變量
class AnimationDemostate extends State<AnimationDemo> with SingleTickerProviderStateMixin {
// SingleTickerProviderStateMixin 性能有關(guān)猫态,需要執(zhí)行Ticker
// 動(dòng)畫控制器需要傳這個(gè)參數(shù),效果是在頁面不可見的區(qū)域披摄,動(dòng)畫不會(huì)執(zhí)行亲雪,避免消耗資源。
Animation animation;
AnimationController _animationController;
}
3.2 初始化動(dòng)畫變量
我們先嘗試讓紅色的正方形在3秒內(nèi)從10的寬高變道300的寬高疚膊,以下代碼中做了以下幾步
- 先創(chuàng)建了一個(gè)動(dòng)畫控制器义辕,
duration
動(dòng)畫的持續(xù)時(shí)間,vsync
這個(gè)就是當(dāng)前的State - 創(chuàng)建了一個(gè)動(dòng)畫函數(shù)
CurvedAnimation
它的是一個(gè)Animation
- 創(chuàng)建
Tween
酿联,begin:10.0终息,end: 300.0,這個(gè)就是在3秒之內(nèi)會(huì)生成10~300的值
new Tween
它是繼承Animatable<T>
贞让,而不是跟CurvedAnimation
一樣繼承Animation周崭;那我們創(chuàng)建的Animatable
如何與Animation
形成映射呢?答案是執(zhí)行animate方法把Animation
傳進(jìn)去,就形成了新的Animation
喳张,我們就可以在Widget
中愉快使用了它了续镇。 - 執(zhí)行動(dòng)畫
_animationController.forward();
class AnimationDemostate extends State<AnimationDemo> with SingleTickerProviderStateMixin{
···
@override
void initState() {
// TODO: implement initState
_animationController = new AnimationController(
duration: Duration(seconds: 3),
vsync: this,
);
animation = new CurvedAnimation(
parent: _animationController,
curve: Curves.ease
);
// Tween begin,end 就是大小變化的區(qū)間
animationSize = new Tween(begin: 10.0, end: 300.0).animate(animation)
..addListener((){
setState(() {
});
});
_animationController.forward();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
···
new Container(
width: animationSize.value,
height: animationSize.value,
decoration: BoxDecoration(
color: Colors.red,
),
)
···
)
}
···
}
經(jīng)過上面步驟之后我們來看下效果:
皆大歡喜销部,我們已經(jīng)成功了第一步摸航!能有動(dòng)畫的效果了,我們繼續(xù)深入后續(xù)的步驟
3.3 讓紅色正方形變大的過程中變成其他顏色
這種相當(dāng)于是關(guān)鍵幀的意思舅桩,就是先變大酱虎,在變大的過程中某個(gè)時(shí)間開始變顏色;在web中我們知道css中有@keyframe
可是設(shè)置關(guān)鍵幀擂涛,來進(jìn)行交織動(dòng)畫读串,flutter中我們?nèi)绾螌?shí)現(xiàn)呢?
在3.2步驟中我們創(chuàng)建過一個(gè)動(dòng)畫曲線CurvedAnimation
,curve
傳的是flutter內(nèi)置的動(dòng)畫函數(shù)Curves.ease
恢暖,在vscode中打出Curves后編輯器會(huì)有提示所有的內(nèi)置的函數(shù)排监;我們要打關(guān)鍵幀的話,關(guān)鍵在于此curve
,這里需要用到Interval(start, end)
,這個(gè)Interval通過start和end我們就能知道了杰捂,它表示的是一段時(shí)間間隔舆床,可以傳入curve
參數(shù)。
了解上面的內(nèi)容之后嫁佳,我們思路應(yīng)該清爽了挨队,那繼續(xù)深入
3.3.1 使正方形變大的過程中由紅色變成藍(lán)色
class xxx extends xxx with xxx {
// 定義變量
Animation animationColor;
// 初始化變量
void initState() {
// TODO: implement initState
_animationController = new AnimationController(
duration: Duration(seconds: 3),
vsync: this,
);
animation = new CurvedAnimation(
parent: _animationController,
curve: new Interval(0, 0.5) // 這里執(zhí)行間隔 是3秒中0~50%的時(shí)間執(zhí)行正方形變大,默認(rèn)動(dòng)畫
);
animationSize = new Tween(begin: 10.0, end: 300.0).animate(animation)
..addListener((){
setState(() {
});
});
animation = new CurvedAnimation(
parent: _animationController,
curve: new Interval(0.5, 1.0, curve: Curves.easeIn) // 50%~100%執(zhí)行變顏色,并且動(dòng)畫是easeIn
);
animationColor = new ColorTween(begin: Colors.white, end: Colors.blue).animate(animation)
..addListener((){
setState(() {
});
});
}
}
通過上面步驟之后
我們來看看最終的的效果:
最后想實(shí)現(xiàn)循環(huán)往復(fù)的效果
需要在動(dòng)畫監(jiān)聽狀態(tài)中去改變動(dòng)畫的方向就可以了
方法如下:
animation.addStatusListener((status){
if (status == AnimationStatus.completed) {
_animationController.reverse();
} else if (status == AnimationStatus.dismissed) {
_animationController.forward();
}
});
來看看效果:
總結(jié)
這邊主要是對(duì)flutter動(dòng)畫的一個(gè)簡(jiǎn)單的入門脱拼,按照上面步驟一步步的去實(shí)現(xiàn)flutter的動(dòng)畫瞒瘸,應(yīng)該就會(huì)對(duì)flutter動(dòng)畫有了一定的了解了坷备,具體深入的了解可以看官方文檔和其他資料的書籍熄浓,如果文中有講述不到位的地方,或者有錯(cuò)誤的地方省撑,也請(qǐng)大佬們多多指教赌蔑,康桑密達(dá)!