Android?Flutter繪制有趣的?loading加載動(dòng)畫(huà)
前言
在網(wǎng)絡(luò)速度較慢的場(chǎng)景,一個(gè)有趣的加載會(huì)提高用戶(hù)的耐心和對(duì) App 的好感,有些 loading
動(dòng)效甚至?xí)層脩?hù)有想弄清楚整個(gè)動(dòng)效過(guò)程到底是怎么樣的沖動(dòng)。然而,大部分的 App的 loading
就是下面這種千篇一律的效果 —— 俗稱(chēng)“轉(zhuǎn)圈”。
本篇我們利用Flutter 的 PathMetric
來(lái)玩幾個(gè)有趣的 loading
效果。
效果1:圓環(huán)內(nèi)滾動(dòng)的球
如上圖所示,一個(gè)紅色的小球在藍(lán)色的圓環(huán)內(nèi)滾動(dòng),而且在往上滾動(dòng)的時(shí)候速度慢,往下滾動(dòng)的時(shí)候有個(gè)明顯的加速過(guò)程。這個(gè)效果實(shí)現(xiàn)的思路如下:
- 繪制一個(gè)藍(lán)色的圓環(huán),在藍(lán)色的圓環(huán)內(nèi)構(gòu)建一個(gè)半徑更小一號(hào)的圓環(huán)路徑(Path)。
- 讓紅色小球在動(dòng)畫(huà)控制下沿著內(nèi)部的圓環(huán)定義的路徑運(yùn)動(dòng)。
- 選擇一個(gè)中間減速(上坡)兩邊加速的動(dòng)畫(huà)曲線。
下面是實(shí)現(xiàn)代碼:
// 動(dòng)畫(huà)控制設(shè)置 controller = AnimationController(duration: const Duration(seconds: 3), vsync: this); animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation( parent: controller, curve: Curves.slowMiddle, )) ..addListener(() { setState(() {}); }); // 繪制和動(dòng)畫(huà)控制方法 _drawLoadingCircle(Canvas canvas, Size size) { var paint = Paint()..style = PaintingStyle.stroke ..color = Colors.blue[400]! ..strokeWidth = 2.0; var path = Path(); final radius = 40.0; var center = Offset(size.width / 2, size.height / 2); path.addOval(Rect.fromCircle(center: center, radius: radius)); canvas.drawPath(path, paint); var innerPath = Path(); final ballRadius = 4.0; innerPath.addOval(Rect.fromCircle(center: center, radius: radius - ballRadius)); var metrics = innerPath.computeMetrics(); paint.color = Colors.red; paint.style = PaintingStyle.fill; for (var pathMetric in metrics) { var tangent = pathMetric.getTangentForOffset(pathMetric.length * animationValue); canvas.drawCircle(tangent!.position, ballRadius, paint); } }
效果2:雙軌運(yùn)動(dòng)
上面的實(shí)現(xiàn)效果其實(shí)比較簡(jiǎn)單,就是繪制了一個(gè)圓和一個(gè)橢圓,然后讓兩個(gè)實(shí)心圓沿著路徑運(yùn)動(dòng)。因?yàn)橛辛诉@個(gè)組合效果,趣味性增加不少,外面的橢圓看起來(lái)就像是一條衛(wèi)星軌道一樣。實(shí)現(xiàn)的邏輯如下:
- 繪制一個(gè)圓和一個(gè)橢圓,二者的中心點(diǎn)重合;
- 在圓和橢圓的路徑上分別繪制一個(gè)小的實(shí)心圓;
- 通過(guò)動(dòng)畫(huà)控制實(shí)心圓沿著大圓和橢圓的路徑上運(yùn)動(dòng)。
具體實(shí)現(xiàn)的代碼如下所示。
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation( parent: controller, curve: Curves.easeInOutSine, )) ..addListener(() { setState(() {}); }); _drawTwinsCircle(Canvas canvas, Size size) { var paint = Paint() ..style = PaintingStyle.stroke ..color = Colors.blue[400]! ..strokeWidth = 2.0; final radius = 50.0; final ballRadius = 6.0; var center = Offset(size.width / 2, size.height / 2); var circlePath = Path() ..addOval(Rect.fromCircle(center: center, radius: radius)); paint.style = PaintingStyle.stroke; paint.color = Colors.blue[400]!; canvas.drawPath(circlePath, paint); var circleMetrics = circlePath.computeMetrics(); for (var pathMetric in circleMetrics) { var tangent = pathMetric .getTangentForOffset(pathMetric.length * animationValue); paint.style = PaintingStyle.fill; paint.color = Colors.blue; canvas.drawCircle(tangent!.position, ballRadius, paint); } paint.style = PaintingStyle.stroke; paint.color = Colors.green[600]!; var ovalPath = Path() ..addOval(Rect.fromCenter(center: center, width: 3 * radius, height: 40)); canvas.drawPath(ovalPath, paint); var ovalMetrics = ovalPath.computeMetrics(); for (var pathMetric in ovalMetrics) { var tangent = pathMetric.getTangentForOffset(pathMetric.length * animationValue); paint.style = PaintingStyle.fill; canvas.drawCircle(tangent!.position, ballRadius, paint); } }
效果3:鐘擺運(yùn)動(dòng)
鐘擺運(yùn)動(dòng)的示意圖如下所示,一條繩子系著一個(gè)球懸掛某處,把球拉起一定的角度釋放后,球就會(huì)帶動(dòng)繩子沿著一條圓弧來(lái)回運(yùn)動(dòng),這條圓弧的半徑就是繩子的長(zhǎng)度。
這個(gè)效果通過(guò)代碼來(lái)實(shí)現(xiàn)的話(huà),需要做下面的事情:
- 繪制頂部的橫線,代表懸掛的頂點(diǎn);
- 繪制運(yùn)動(dòng)的圓弧路徑,以便讓球沿著圓弧運(yùn)動(dòng);
- 繪制實(shí)心圓代表球,并通過(guò)動(dòng)畫(huà)控制沿著一條圓弧運(yùn)動(dòng);
- 用一條頂端固定,末端指向球心的直線代表繩子;
- 當(dāng)球運(yùn)動(dòng)到弧線的終點(diǎn)后,通過(guò)動(dòng)畫(huà)反轉(zhuǎn)(
reverse
)控制球 返回;到起點(diǎn)后再正向(forward
) 運(yùn)動(dòng)就可以實(shí)現(xiàn)來(lái)回運(yùn)動(dòng)的效果了。
具體實(shí)現(xiàn)的代碼如下,這里在繪制球的時(shí)候給 Paint
對(duì)象增加了一個(gè) maskFilter
屬性,以便讓球看起來(lái)發(fā)光,更加好看點(diǎn)。
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); animation = Tween<double>(begin: 0, end: 1.0).animate(CurvedAnimation( parent: controller, curve: Curves.easeInOutQuart, )) ..addListener(() { setState(() {}); } ..addStatusListener((status) { if (status == AnimationStatus.completed) { controller.reverse(); } else if (status == AnimationStatus.dismissed) { controller.forward(); } }); _drawPendulum(Canvas canvas, Size size) { var paint = Paint() ..style = PaintingStyle.stroke ..color = Colors.blue[400]! ..strokeWidth = 2.0; final ceilWidth = 60.0; final pendulumHeight = 200.0; var ceilCenter = Offset(size.width / 2, size.height / 2 - pendulumHeight / 2); var ceilPath = Path() ..moveTo(ceilCenter.dx - ceilWidth / 2, ceilCenter.dy) ..lineTo(ceilCenter.dx + ceilWidth / 2, ceilCenter.dy); canvas.drawPath(ceilPath, paint); var pendulumArcPath = Path() ..addArc(Rect.fromCircle(center: ceilCenter, radius: pendulumHeight), 3 * pi / 4, -pi / 2); paint.color = Colors.white70; var metrics = pendulumArcPath.computeMetrics(); for (var pathMetric in metrics) { var tangent = pathMetric.getTangentForOffset(pathMetric.length * animationValue); canvas.drawLine(ceilCenter, tangent!.position, paint); paint.style = PaintingStyle.fill; paint.color = Colors.blue; paint.maskFilter = MaskFilter.blur(BlurStyle.solid, 4.0); canvas.drawCircle(tangent.position, 16.0, paint); } }
總結(jié)
本篇介紹了三種 Loading 動(dòng)效的繪制邏輯和實(shí)現(xiàn)代碼,可以看到利用路徑屬性進(jìn)行繪圖以及動(dòng)畫(huà)控制可以實(shí)現(xiàn)很多有趣的動(dòng)畫(huà)效果。
以上就是Android Flutter繪制有趣的 loading加載動(dòng)畫(huà)的詳細(xì)內(nèi)容,更多關(guān)于Android Flutter加載動(dòng)畫(huà)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android應(yīng)用圖標(biāo)在狀態(tài)欄上顯示實(shí)現(xiàn)原理
Android應(yīng)用圖標(biāo)在狀態(tài)欄上顯示,以及顯示不同的圖標(biāo),其實(shí)很研究完后,才發(fā)現(xiàn),很簡(jiǎn)單,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈2013-06-06android使用handlerthread創(chuàng)建線程示例
這篇文章主要介紹了android使用handlerthread創(chuàng)建線程,講解了這種方式的好處及為什么不使用Thread類(lèi)的原因2014-01-01Android 退出應(yīng)用程序的實(shí)現(xiàn)方法
這篇文章主要介紹了Android 退出應(yīng)用程序的實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2017-04-04Kotlin中常見(jiàn)內(nèi)聯(lián)擴(kuò)展函數(shù)的使用方法教程
在Kotlin中,使用inline修飾符標(biāo)記內(nèi)聯(lián)函數(shù),既會(huì)影響到函數(shù)本身, 也影響到傳遞給它的Lambda表達(dá)式,這兩者都會(huì)被內(nèi)聯(lián)到調(diào)用處。下面這篇文章主要給大家介紹了關(guān)于Kotlin中常見(jiàn)內(nèi)聯(lián)擴(kuò)展函數(shù)的使用方法,需要的朋友可以參考下。2017-12-12Android ViewModel與Lifecycles和LiveData組件用法詳細(xì)講解
JetPack是一個(gè)開(kāi)發(fā)組件工具集,他的主要目的是幫助我們編寫(xiě)出更加簡(jiǎn)潔的代碼,并簡(jiǎn)化我們的開(kāi)發(fā)過(guò)程。JetPack中的組件有一個(gè)特點(diǎn),它們大部分不依賴(lài)于任何Android系統(tǒng)版本,這意味者這些組件通常是定義在AndroidX庫(kù)當(dāng)中的,并且擁有非常好的向下兼容性2023-01-01