網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
在網(wǎng)絡(luò)速度較慢的場(chǎng)景,一個(gè)有趣的加載會(huì)提高用戶的耐心和對(duì) App 的好感,有些 loading
動(dòng)效甚至?xí)層脩粲邢肱宄麄€(gè)動(dòng)效過(guò)程到底是怎么樣的沖動(dòng)。然而,大部分的 App的 loading
就是下面這種千篇一律的效果 —— 俗稱“轉(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)的話,需要做下面的事情:
- 繪制頂部的橫線,代表懸掛的頂點(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à)效果。
原文鏈接:https://juejin.cn/post/7125029565625270286
相關(guān)推薦
- 2022-10-07 React中父組件如何獲取子組件的值或方法_React
- 2022-09-20 C#利用TreeView控件實(shí)現(xiàn)目錄跳轉(zhuǎn)_C#教程
- 2022-04-16 C語(yǔ)言棧之順序棧_C 語(yǔ)言
- 2022-11-18 Redis?存儲(chǔ)對(duì)象信息用?Hash?和String的區(qū)別_Redis
- 2022-04-15 Go?interface{}?轉(zhuǎn)切片類型的實(shí)現(xiàn)方法_Golang
- 2022-10-02 jQuery?編程之jQuery?屬性選擇器_jquery
- 2022-05-12 Android 截屏實(shí)現(xiàn)、屏幕截圖、MediaProjection、ImageReader
- 2022-01-04 微信小程序內(nèi)部A頁(yè)面向內(nèi)嵌H5頁(yè)面跳轉(zhuǎn),并且傳參
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支