網站首頁 編程語言 正文
前言
在上一篇普通的加載千篇一律,有趣的 loading 萬里挑一 中,我們介紹了使用Path
類的PathMetrics
屬性來控制繪制點在路徑上運動來實現比較有趣的loading
效果。有評論說因為是黑色背景,所以看著好看。黑色背景確實顯得高端一點,但是并不是其他配色也不行,本篇我們來封裝一個可以自定義配置前景色和背景色的Loading
組件。
組件定義
loading
組件共定義4個入口參數:
- 前景色:繪制圖形的前景色;
- 背景色:繪制圖形的背景色;
- 圖形尺寸:繪制圖形的尺寸;
- 加載文字:可選,如果有文字就顯示,沒有就不顯示。
得到的Loading
組件類如下所示:
class LoadingAnimations extends StatefulWidget { final Color bgColor; final Color foregroundColor; String? loadingText; final double size; LoadingAnimations( {required this.foregroundColor, required this.bgColor, this.loadingText, this.size = 100.0, Key? key}) : super(key: key); @override _LoadingAnimationsState createState() => _LoadingAnimationsState(); }
圓形Loading
我們先來實現一個圓形的loading
,效果如下所示。
這里繪制了兩組沿著一個大圓運動的軸對稱的實心圓,半徑依次減小,圓心間距隨著動畫時間逐步拉大。實際上實現的核心還是基于Path
的PathMetrics
。具體實現代碼如下:
_drawCircleLoadingAnimaion( Canvas canvas, Size size, Offset center, Paint paint) { final radius = boxSize / 2; final ballCount = 6; final ballRadius = boxSize / 15; var circlePath = Path() ..addOval(Rect.fromCircle(center: center, radius: radius)); var circleMetrics = circlePath.computeMetrics(); for (var pathMetric in circleMetrics) { for (var i = 0; i < ballCount; ++i) { var lengthRatio = animationValue * (1 - i / ballCount); var tangent = pathMetric.getTangentForOffset(pathMetric.length * lengthRatio); var ballPosition = tangent!.position; canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint); canvas.drawCircle( Offset(size.width - tangent.position.dx, size.height - tangent.position.dy), ballRadius / (1 + i), paint); } } }
其中路徑比例為lengthRatio
,通過animationValue
乘以一個系數使得實心圓的間距越來越大 ,同時通過Offset(size.width - tangent.position.dx, size.height - tangent.position.dy)
繪制了一組對對稱的實心圓,這樣整體就有一個圓形的效果了,動起來也會更有趣一點。
橢圓運動Loading
橢圓和圓形沒什么區別,這里我們搞個漸變的效果看看,利用之前介紹過的Paint
的shader
可以實現漸變色繪制效果。
實現代碼如下所示。
final ballCount = 6; final ballRadius = boxSize / 15; var ovalPath = Path() ..addOval(Rect.fromCenter( center: center, width: boxSize, height: boxSize / 1.5)); paint.shader = LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [this.foregroundColor, this.bgColor], ).createShader(Offset.zero & size); var ovalMetrics = ovalPath.computeMetrics(); for (var pathMetric in ovalMetrics) { for (var i = 0; i < ballCount; ++i) { var lengthRatio = animationValue * (1 - i / ballCount); var tangent = pathMetric.getTangentForOffset(pathMetric.length * lengthRatio); var ballPosition = tangent!.position; canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint); canvas.drawCircle( Offset(size.width - tangent.position.dx, size.height - tangent.position.dy), ballRadius / (1 + i), paint); } }
當然,如果漸變色的顏色更豐富一點會更有趣些。
貝塞爾曲線Loading
通過貝塞爾曲線構建一條Path
,讓一組圓形沿著貝塞爾曲線運動的Loading
效果也很有趣。
原理和圓形的一樣,首先是構建貝塞爾曲線Path
,代碼如下。
var bezierPath = Path() ..moveTo(size.width / 2 - boxSize / 2, center.dy) ..quadraticBezierTo(size.width / 2 - boxSize / 4, center.dy - boxSize / 4, size.width / 2, center.dy) ..quadraticBezierTo(size.width / 2 + boxSize / 4, center.dy + boxSize / 4, size.width / 2 + boxSize / 2, center.dy) ..quadraticBezierTo(size.width / 2 + boxSize / 4, center.dy - boxSize / 4, size.width / 2, center.dy) ..quadraticBezierTo(size.width / 2 - boxSize / 4, center.dy + boxSize / 4, size.width / 2 - boxSize / 2, center.dy);
這里實際是構建了兩條貝塞爾曲線,先從左邊到右邊,然后再折回來。之后就是運動的實心圓了,這個只是數量上多了,ballCount
為30
,這樣效果看著就有一種拖影的效果。
var ovalMetrics = bezierPath.computeMetrics(); for (var pathMetric in ovalMetrics) { for (var i = 0; i < ballCount; ++i) { var lengthRatio = animationValue * (1 - i / ballCount); var tangent = pathMetric.getTangentForOffset(pathMetric.length * lengthRatio); var ballPosition = tangent!.position; canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint); canvas.drawCircle( Offset(size.width - tangent.position.dx, size.height - tangent.position.dy), ballRadius / (1 + i), paint); } }
這里還可以改變運動方向,實現一些其他的效果,例如下面的效果,第二組圓球的繪制位置實際上是第一組圓球的x、y坐標的互換。
var lengthRatio = animationValue * (1 - i / ballCount); var tangent = pathMetric.getTangentForOffset(pathMetric.length * lengthRatio); var ballPosition = tangent!.position; canvas.drawCircle(ballPosition, ballRadius / (1 + i), paint); canvas.drawCircle(Offset(tangent.position.dy, tangent.position.dx), ballRadius / (1 + i), paint);
組件使用
我們來看如何使用我們定義的這個組件,使用代碼如下,我們用Future延遲模擬了一個加載效果,在加載過程中使用loading
指示加載過程,加載完成后顯示圖片。
class _LoadingDemoState extends State<LoadingDemo> { var loaded = false; @override void initState() { super.initState(); Future.delayed(Duration(seconds: 5), () { setState(() { loaded = true; }); }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Loading 使用'), ), body: Center( child: loaded ? Image.asset( 'images/beauty.jpeg', width: 100.0, ) : LoadingAnimations( foregroundColor: Colors.blue, bgColor: Colors.white, size: 100.0, ), ), ); }
最終運行的效果如下,源碼已提交至:繪圖相關源碼,文件名為loading_animations.dart
。
總結
本篇介紹了Loading
組件的封裝方法,核心要點還是利用Path
和動畫控制繪制元素的運動軌跡來實現更有趣的效果。在實際應用過程中,也可以根據交互設計的需要,做一些其他有趣的加載動效,提高等待過程的趣味性。
原文鏈接:https://juejin.cn/post/7129071400509243423
相關推薦
- 2022-12-24 C++返回值是類名和返回值是引用的區別及說明_C 語言
- 2022-01-05 npm ERR! code ENOENT npm ERR! syscall open npm ERR
- 2022-02-07 出現報錯nginx: [emerg] unknown directive nginx.htacces
- 2022-05-05 Python學習之流程控制與條件判斷總結_python
- 2023-01-03 Android?自定義Livedata使用示例解析_Android
- 2022-07-14 C++實現一個簡單的線程池的示例代碼_C 語言
- 2022-09-08 pytorch?tensor內所有元素相乘實例_python
- 2022-05-12 Kotlin List的創建與取值 getOrElse getOrNull
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支