普通的載入千篇一律,有趣的 loading 萬里挑一
theme: v-green highlight: atom-one-dark
攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第1天,點選檢視活動詳情
前言
在網路速度較慢的場景,一個有趣的載入會提高使用者的耐心和對 App 的好感,有些 loading
動效甚至會讓使用者有想弄清楚整個動效過程到底是怎麼樣的衝動。然而,大部分的 App的 loading
就是下面這種千篇一律的效果 —— 俗稱“轉圈”。
本篇我們利用Flutter 的 PathMetric
來玩幾個有趣的 loading
效果。
效果1:圓環內滾動的球
如上圖所示,一個紅色的小球在藍色的圓環內滾動,而且在往上滾動的時候速度慢,往下滾動的時候有個明顯的加速過程。這個效果實現的思路如下:
- 繪製一個藍色的圓環,在藍色的圓環內構建一個半徑更小一號的圓環路徑(Path)。
- 讓紅色小球在動畫控制下沿著內部的圓環定義的路徑運動。
- 選擇一箇中間減速(上坡)兩邊加速的動畫曲線。
下面是實現程式碼:
```dart
// 動畫控制設定
controller =
AnimationController(duration: const Duration(seconds: 3), vsync: this);
animation = Tween
// 繪製和動畫控制方法 _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:雙軌運動
上面的實現效果其實比較簡單,就是繪製了一個圓和一個橢圓,然後讓兩個實心圓沿著路徑運動。因為有了這個組合效果,趣味性增加不少,外面的橢圓看起來就像是一條衛星軌道一樣。實現的邏輯如下:
- 繪製一個圓和一個橢圓,二者的中心點重合;
- 在圓和橢圓的路徑上分別繪製一個小的實心圓;
- 通過動畫控制實心圓沿著大圓和橢圓的路徑上運動。
具體實現的程式碼如下所示。
```dart
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween
_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:鐘擺運動
鐘擺運動的示意圖如下所示,一條繩子繫著一個球懸掛某處,把球拉起一定的角度釋放後,球就會帶動繩子沿著一條圓弧來回運動,這條圓弧的半徑就是繩子的長度。 這個效果通過程式碼來實現的話,需要做下面的事情:
- 繪製頂部的橫線,代表懸掛的頂點;
- 繪製運動的圓弧路徑,以便讓球沿著圓弧運動;
- 繪製實心圓代表球,並通過動畫控制沿著一條圓弧運動;
- 用一條頂端固定,末端指向球心的直線代表繩子;
- 當球運動到弧線的終點後,通過動畫反轉(
reverse
)控制球 返回;到起點後再正向(forward
) 運動就可以實現來回運動的效果了。
具體實現的程式碼如下,這裡在繪製球的時候給 Paint
物件增加了一個 maskFilter
屬性,以便讓球看起來發光,更加好看點。
```dart
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween
_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);
} } ```
總結
本篇介紹了三種 Loading 動效的繪製邏輯和實現程式碼,可以看到利用路徑屬性進行繪圖以及動畫控制可以實現很多有趣的動畫效果。
我是島上碼農,微信公眾號同名,這是Flutter 入門與實戰的專欄文章,提供體系化的 Flutter 學習文章。對應原始碼請看這裡:Flutter 入門與實戰專欄原始碼。如有問題可以加本人微信交流,微訊號:
island-coder
。👍🏻:覺得有收穫請點個贊鼓勵一下!
🌟:收藏文章,方便回看哦!
💬:評論交流,互相進步!
- 屌炸天!國外同行這樣用Chat GPT提高Flutter開發的效率!
- Flutter 增強版的頁面懸浮按鈕(FloatingActionButton)
- 介紹一個令強迫症討厭的小紅點元件
- 我用了幾行程式碼就搞定了介面變灰效果
- 不就是一個空白頁,有必要那麼講究嗎?
- 花裡胡哨的文字特效,你學會了嗎?
- 這一篇讓你搞定 Flutter 的資料表格
- 用 Flutter 輕鬆做個紅包介面
- 沉浸式彈層越來越多,開發該怎麼做好彈層體驗?
- 列表的載入過程很重要的!
- 這個表單打死我也不填!
- 例項講述開發中的圖片使用者體驗要點
- C 位出道按鈕的自我獨白
- Flutter 繪製3D效果動畫
- 封裝一個有趣的 Loading 元件
- 普通的載入千篇一律,有趣的 loading 萬里挑一
- 由點匯聚成字的動效炫極了
- 給滅霸點顏色看看
- 來看光影流動之美
- Flutter 實現背景圖片毛玻璃效果