基于Flutter制作一個(gè)吃豆人加載動(dòng)畫
效果圖
國際慣例,先看效果圖:

具體效果就是吃豆人會(huì)根據(jù)吃不同顏色的豆子改變身體的顏色。
繪制靜態(tài)吃豆人、豆豆、眼睛
首先,我們需要將這個(gè)靜態(tài)的吃豆人繪制出來,我們可以把吃豆人看做是一個(gè)實(shí)心圓弧,豆豆和眼睛就是一個(gè)圓。
關(guān)鍵代碼:
//畫頭
_paint
..color = color.value
..style = PaintingStyle.fill;
var rect = Rect.fromCenter(
center: Offset(0, 0), width: size.width, height: size.height);
/// 起始角度
var a = 40 / 180 * pi;
// 繪制圓弧
canvas.drawArc(rect, 0, 2 * pi - a * 2, true, _paint);
// 畫豆豆
canvas.drawOval(
Rect.fromCenter(
center: Offset(
size.width / 2 +
ddSize -
angle2.value * (size.width / 2 + ddSize),
0),
width: ddSize,
height: ddSize),
_paint..color = color2.value);
//畫眼睛
canvas.drawOval(
Rect.fromCenter(
center: Offset(0, -size.height / 3), width: 8, height: 8),
_paint..color = Colors.black87);動(dòng)畫屬性: 嘴巴的張合:通過圓弧的角度不斷改變實(shí)現(xiàn),豆豆移動(dòng):從頭的右側(cè)源源不斷的有豆子向左移動(dòng),改變豆豆x軸的坐標(biāo)即可,接下來我們讓吃豆人動(dòng)起來吧。
加入動(dòng)畫屬性
這里我們需要?jiǎng)?chuàng)建2個(gè)動(dòng)畫控制器,一個(gè)控制頭,一個(gè)控制豆豆,我們看到因?yàn)轭^部一開一合屬于動(dòng)畫正向執(zhí)行一次然后再反向執(zhí)行一次,相當(dāng)于執(zhí)行了兩次,豆豆的從右邊到嘴巴只執(zhí)行了一次,所以頭的執(zhí)行時(shí)間是豆豆執(zhí)行時(shí)間的兩倍,嘴巴一張一合才能吃豆子嘛,吃豆完畢,將豆子顏色賦值給頭改變顏色,豆子隨機(jī)獲取另一個(gè)顏色,不斷的吃豆。 這里的繪制狀態(tài)有多種情況,嘴巴的張合、豆子的平移、顏色的改變都需要進(jìn)行重新繪制,這里我們可以使用 Listenable.merge方法來進(jìn)行監(jiān)聽,接受一個(gè)Listenable數(shù)組,可以將我們需要改變的狀態(tài)放到這個(gè)數(shù)組里,返回一個(gè) Listenable賦值給CustomPainter構(gòu)造函數(shù)repaint屬性即可,然后在監(jiān)聽只需判斷這個(gè)Listenable即可。
factory Listenable.merge(List<Listenable?> listenables) = _MergingListenable;
關(guān)鍵代碼: 動(dòng)畫執(zhí)行相關(guān)。
late Animation<double> animation; // 吃豆人
late Animation<double> animation2; // 豆豆
late AnimationController _controller = AnimationController(
vsync: this, duration: Duration(milliseconds: 500)); //1s
late AnimationController _controller2 = AnimationController(
vsync: this, duration: Duration(milliseconds: 1000)); //2s
//初始化吃豆人、豆豆顏色
ValueNotifier<Color> _color = ValueNotifier<Color>(Colors.yellow.shade800);
ValueNotifier<Color> _color2 =
ValueNotifier<Color>(Colors.redAccent.shade400);
// 動(dòng)畫軌跡
late CurvedAnimation cure = CurvedAnimation(
parent: _controller, curve: Curves.easeIn); // 動(dòng)畫運(yùn)行的速度軌跡 速度的變化
@override
void initState() {
super.initState();
animation = Tween(begin: 0.2, end: 1.0).animate(_controller)
..addStatusListener((status) {
// dismissed 動(dòng)畫在起始點(diǎn)停止
// forward 動(dòng)畫正在正向執(zhí)行
// reverse 動(dòng)畫正在反向執(zhí)行
// completed 動(dòng)畫在終點(diǎn)停止
if (status == AnimationStatus.completed) {
_controller.reverse(); //反向執(zhí)行 100-0
} else if (status == AnimationStatus.dismissed) {
_color.value = _color2.value;
// 獲取一個(gè)隨機(jī)彩虹色
_color2.value = getRandomColor();
_controller.forward(); //正向執(zhí)行 0-100
// 豆子已經(jīng)被吃了 從新加載豆子動(dòng)畫
_controller2.forward(from: 0); //正向執(zhí)行 0-100
}
});
animation2 = Tween(begin: 0.2, end: 1.0).animate(_controller2);
// 啟動(dòng)動(dòng)畫 正向執(zhí)行
_controller.forward();
// 啟動(dòng)動(dòng)畫 0-1循環(huán)執(zhí)行
_controller2.forward();
// 這里這樣重復(fù)調(diào)用會(huì)導(dǎo)致兩次動(dòng)畫執(zhí)行時(shí)間不一致 時(shí)間長了就不對(duì)應(yīng)了
// _controller2.repeat();
}
@override
void dispose() {
_controller.dispose();
_controller2.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Center(
child: CustomPaint(
size: Size(50, 50),
painter: Pain2Painter(
_color,
_color2,
animation,
animation2,
Listenable.merge([
animation,
animation2,
_color,
]),
ddSize: 8),
));
}
// 獲取一個(gè)隨機(jī)顏色
Color getRandomColor() {
Random random = Random.secure();
int randomInt = random.nextInt(6);
var colors = <Color>[
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.indigo,
Colors.purple,
];
Color color = colors[randomInt];
while (color == _color2.value) {
// 重復(fù)再選一個(gè)
color = colors[random.nextInt(6)];
}
return color;
}繪制吃豆人源碼:
class Pain2Painter extends CustomPainter {
final ValueNotifier<Color> color; // 吃豆人的顏色
final ValueNotifier<Color> color2; // 豆子的的顏色
final Animation<double> angle; // 吃豆人
final Animation<double> angle2; // 豆
final double ddSize; // 豆豆大小
final Listenable listenable;
Pain2Painter(
this.color, this.color2, this.angle, this.angle2, this.listenable,
{this.ddSize = 6})
: super(repaint: listenable);
Paint _paint = Paint();
@override
void paint(Canvas canvas, Size size) {
canvas.clipRect(Offset.zero & size);
canvas.translate(size.width / 2, size.height / 2);
// 畫豆豆
canvas.drawOval(
Rect.fromCenter(
center: Offset(
size.width / 2 +
ddSize -
angle2.value * (size.width / 2 + ddSize),
0),
width: ddSize,
height: ddSize),
_paint..color = color2.value);
//畫頭
_paint
..color = color.value
..style = PaintingStyle.fill;
var rect = Rect.fromCenter(
center: Offset(0, 0), width: size.width, height: size.height);
/// 起始角度
/// angle.value 動(dòng)畫控制器的值 0.2~1 0是完全閉合就是 起始0~360° 1是完全張開 起始 40°~ 280° 順時(shí)針
var a = angle.value * 40 / 180 * pi;
// 繪制圓弧
canvas.drawArc(rect, a, 2 * pi - a * 2, true, _paint);
//畫眼睛
canvas.drawOval(
Rect.fromCenter(
center: Offset(0, -size.height / 3), width: 8, height: 8),
_paint..color = Colors.black87);
canvas.drawOval(
Rect.fromCenter(
center: Offset(-1.5, -size.height / 3 - 1.5), width: 3, height: 3),
_paint..color = Colors.white);
}
@override
bool shouldRepaint(covariant Pain2Painter oldDelegate) {
return oldDelegate.listenable != listenable;
}
}至此,一個(gè)簡單的吃豆人加載Loading就完成啦。再也不要到處都是菊花轉(zhuǎn)的樣式了。。。
總結(jié)
通過這個(gè)加載Loading動(dòng)畫可以重新復(fù)習(xí)下Flutter中繪制、動(dòng)畫的使用的聯(lián)動(dòng)使用、還有多狀態(tài)重繪機(jī)制,通過動(dòng)畫還可以改變吃豆的速度和吃豆的時(shí)間運(yùn)動(dòng)軌跡,有興趣可以試試哦。
以上就是基于Flutter制作一個(gè)吃豆人加載動(dòng)畫的詳細(xì)內(nèi)容,更多關(guān)于Flutter加載動(dòng)畫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android仿淘寶商品拖動(dòng)查看詳情及標(biāo)題欄漸變功能
這篇文章主要介紹了Android仿淘寶商品拖動(dòng)查看詳情及標(biāo)題欄漸變功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Android Studio 3.0上分析內(nèi)存泄漏的原因
本篇文章主要介紹了Android Studio 3.0上分析內(nèi)存泄漏的原因,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
Android開發(fā)OkHttp執(zhí)行流程源碼分析
這篇文章主要為大家介紹了Android開發(fā)OkHttp執(zhí)行流程源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
使用Android實(shí)現(xiàn)一個(gè)懸浮在軟鍵盤上的輸入欄
app開發(fā)中經(jīng)常會(huì)遇到一些輸入框要懸浮到軟鍵盤上方的需求,下面這篇文章主要給大家介紹了關(guān)于如何使用Android實(shí)現(xiàn)一個(gè)懸浮在軟鍵盤上的輸入欄的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
Android編程實(shí)現(xiàn)手機(jī)自帶內(nèi)部存儲(chǔ)路徑的獲取方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)手機(jī)自帶內(nèi)部存儲(chǔ)路徑的獲取方法,涉及Android針對(duì)掛載點(diǎn)信息的獲取技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
MotionLayout自定義開關(guān)按鈕實(shí)例詳解
這篇文章主要為大家介紹了MotionLayout自定義開關(guān)按鈕實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Android?hid發(fā)送apdu格式數(shù)據(jù)示例詳解
這篇文章主要介紹了Android?hid發(fā)送apdu格式數(shù)據(jù),在?Android?中,如果你想通過?HID(Human?Interface?Device)發(fā)送?APDU?格式的數(shù)據(jù),通常會(huì)涉及?USB?HID?設(shè)備或藍(lán)牙?HID?設(shè)備,本文給大家講解的非常詳細(xì),需要的朋友可以參考下2023-08-08
Android 實(shí)現(xiàn)抖音小游戲潛艇大挑戰(zhàn)的思路詳解
《潛水艇大挑戰(zhàn)》是抖音上的一款小游戲,最近特別火爆,很多小伙伴都玩過。接下來通過本文給大家分享Android 手?jǐn)]抖音小游戲潛艇大挑戰(zhàn)的思路,需要的朋友可以參考下2020-04-04
Android中實(shí)現(xiàn)iOS中的毛玻璃效果
為了實(shí)現(xiàn)毛玻璃效果,我們需要一組compute kernels(.rs文件中編寫),及一組用于控制renderScript相關(guān)的Javaapi(.rs文件自動(dòng)生成為Java類)。 這篇文章主要介紹了Android中實(shí)現(xiàn)iOS中的毛玻璃效果,需要的朋友可以參考下2017-06-06

