網(wǎng)站首頁 編程語言 正文
粒子運(yùn)動概念
粒子運(yùn)動是將對象按照一定物理公式進(jìn)行的自定義軌跡運(yùn)動,與普通動畫不同的是,它沒有強(qiáng)制性的動畫開始到結(jié)束的時間概念,因為粒子的運(yùn)動開始到結(jié)束的時間并不是固定的,而是由具體場景的物理運(yùn)動公式來決定的,什么時候結(jié)束由你來定,例如:小球自由落體彈跳動畫松開小球開始到地面停止的時間就跟距離地面初始高度有關(guān),初始高度越高,動畫時間越長,反之依然,所以,粒子運(yùn)動可以說是符合物理公式并持續(xù)不斷的動畫。
粒子運(yùn)動特點:符合物理運(yùn)動公式、持續(xù)不斷運(yùn)動。
如何保持持續(xù)運(yùn)動?
我們可以通過動畫控制器AnimationController
調(diào)用repeat();
方法開啟無限循環(huán)動畫來實現(xiàn),這里時間設(shè)置多少都行,因為我們不用它,而是用addListener()
這個方法來觸發(fā)小球運(yùn)動,這個方法可以理解為粒子運(yùn)動的刷新率,通常1
秒觸發(fā)回調(diào)60
次,通過這個回調(diào)我們就可以持續(xù)不斷的驅(qū)使小球改運(yùn)動。
late AnimationController _controller; _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, ) ..addListener((){ // 通常這個回調(diào)會一秒回調(diào)60次 也就是我們平常的60hz屏幕刷新率。 })..repeat();
創(chuàng)建粒子對象
理解了上方的信息,接下來我們首先創(chuàng)建一個粒子對象,粒子對象包含粒子運(yùn)動所需速度、加速度、位移等信息。
代碼:
// 粒子對象 class Particle { double x; // x軸位移. double ax; // 粒子水平加速度 double vx; //粒子水平速度 double y; // y軸位移. double ay; // 粒子豎直加速度 double vy; //粒子豎直速度 double maxY;//最大垂直彈跳高度 double size; // 粒子大小. Color color; // 粒子顏色. Particle({ this.x = 0, this.ax = 0, this.vx = 0, this.y = 0, this.ay = 0, this.vy = 0, this.size = 0, this.maxY = 0, this.color = Colors.blue, }); }
創(chuàng)建粒子控制器
有了粒子對象,接下來創(chuàng)建粒子控制器,混入ChangeNotifier
通過改變粒子屬性通知畫板刷新,這里通過update
方法改變小球的運(yùn)動軌跡。
我們知道自由落體彈跳,由于地心引力和能量守恒,在沒有外力的加持下,小球落地彈起的過程是一個加速 - 彈起 - 減速 - 速度為0 - 再加速...的過程,最終小球相對地面達(dá)到靜止?fàn)顟B(tài),那么我們假設(shè)小球垂直自由落體彈跳,由于能量的損失,小球彈起速度為下落撞擊地面速度的4/5
,那么隨著時間的推移,小球的速度就會越來越慢,直到靜止。
代碼:
// 粒子控制器 class ParticleController with ChangeNotifier { // 粒子 late Particle p; // 粒子運(yùn)動區(qū)域 Size? size; ParticleController(); void update() { // 此方法一秒刷新60次 // 距離= 時間 * 速度。 p.y += p.vy; // 自由落體 速度不斷加快,地球加速度9.8/s p.vy += p.ay; if (p.y > size!.height) { // 反彈高度為之前4/5 p.maxY = p.maxY * 0.8; p.y = size!.height; // 假設(shè)能量損失 反彈速度為下落最大速度的4/5 p.vy = -p.vy * 0.8; } if (p.y < size!.height - p.maxY) { p.y = size!.height - p.maxY; p.vy = 0; } if (p.maxY < 0.01) { // 如果小球距離地面小于0.01 我們認(rèn)為小球已達(dá)到靜止?fàn)顟B(tài),動畫結(jié)束 恢復(fù)初始高度,以及最大高度 p.y = p.initY; p.maxY = size!.height; } notifyListeners(); } }
初始化粒子
創(chuàng)建粒子控制器,初始化粒子,設(shè)置粒子初始位移、初始速度,加速度等信息,并將粒子控制器傳給畫板。
late AnimationController _controller; ParticleController pController = ParticleController(); @override void initState() { super.initState(); // 初始化 initParticleController(); _controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, ) ..addListener(() { pController.update(); }) ..repeat(); } void initParticleController() { pController.size = Size(300, 200); Particle particle = Particle( // 初始高度 y: 0, // 初始速度 vy: 0, // 由于地球加速度為9.8m/s,這里1s觸發(fā)60次 所以要除以60. ay: 9.8 / 60, // 最大彈跳高度 maxY: pController.size!.height, color: Colors.blue, size: 8); pController.p = particle; } @override Widget build(BuildContext context) { return CustomPaint( size: Size(double.infinity, double.infinity), painter: _BallMove(controller: pController), ); } }
創(chuàng)建畫板
創(chuàng)建畫板,繪制小球和輔助區(qū)域,小球圓心為粒子位移的距離。
class _BallMove extends CustomPainter { // final ParticleController controller; Paint ballPaint = Paint(); Paint stokePaint = Paint() ..strokeWidth = 0.5 ..style = PaintingStyle.stroke; // 實現(xiàn)super方法 實現(xiàn)刷新 _BallMove({required this.controller}) : super(repaint: controller); @override void paint(Canvas canvas, Size size) { canvas.translate(size.width / 2, size.height / 2); canvas.save(); canvas.translate(0, controller.size!.height / 2); // 小球運(yùn)動區(qū)域 canvas.drawRect( Rect.fromCenter( center: Offset.zero, width: controller.size!.width, height: controller.size!.height), stokePaint); canvas.restore(); // 設(shè)置小球顏色 ballPaint.color = controller.p.color; canvas.drawCircle(Offset(controller.p.x, controller.p.y), controller.p.size, ballPaint); } @override bool shouldRepaint(covariant _BallMove oldDelegate) { return false; } }
效果:
這樣就實現(xiàn)了小球自由落體彈跳效果,當(dāng)然這只是理想的狀態(tài)下的自由落體,真實狀態(tài)下有很多因素的影響,像空氣阻力、風(fēng)等因素。上面只是實現(xiàn)了一個粒子的自由落體,加速度為地球重力加速度,多粒子運(yùn)動原理一樣。
多粒子實現(xiàn)八大行星加速度自由落體彈跳
修改粒子控制器增加粒子集合,實現(xiàn)多粒子運(yùn)動,
// 粒子集合 List<Particle> particles = []; void update() { // 循環(huán)粒子集合 particles.forEach(doUpdate); notifyListeners(); } void doUpdate(Particle p) { // 一秒刷新60次 // 距離= 時間 * 速度。 // 自由落體 速度不斷加快,地球加速度9.8/s // s = t * v; p.y += p.vy; p.vy += p.ay; if (p.y > size!.height) { p.maxY = p.maxY * 0.8; p.y = size!.height; // 假設(shè)能量損失 反彈速度為彈起的4/5 p.vy = -p.vy * 0.8; } if (p.y < size!.height - p.maxY) { p.y = size!.height - p.maxY; p.vy = 0; } if (p.maxY < 0.01) { p.y = p.initY; p.maxY = size!.height; } }
已知各大行星加速度為:
- 水星:3.7m/s。 金星:8.87m/s。
- 地球:9.8m/s。 火星:3.71m/s。
- 木星:24.79m/s。 土星:10.44m/s。
- 天王星:8.87m/s。 海王星:11.15m/s。
初始化八大行星集合。
void initParticleController() { pController.size = Size(300, 200); // 修改 ay為各大行星的加速度 Particle particle1 = Particle( x: -140, ay: 3.7 / 60, maxY: pController.size!.height, color: Colors.green, size: 8); Particle particle2 = Particle( x: -100, ay: 8.87 / 60, maxY: pController.size!.height, color: Colors.yellow, size: 8); Particle particle3 = Particle( x: -60, ay: 9.8 / 60, maxY: pController.size!.height, color: Colors.blue, size: 8); Particle particle4 = Particle( x: -20, ay: 3.71 / 60, maxY: pController.size!.height, color: Colors.red, size: 8); Particle particle5 = Particle( x: 20, ay: 24.79 / 60, maxY: pController.size!.height, color: Colors.cyan, size: 8); Particle particle6 = Particle( x: 60, ay: 10.44 / 60, maxY: pController.size!.height, color: Colors.orangeAccent, size: 8); Particle particle7 = Particle( x: 100, ay: 8.87 / 60, maxY: pController.size!.height, color: Colors.blueGrey, size: 8); Particle particle8= Particle( x: 140, ay: 11.15/ 60, maxY: pController.size!.height, color: Colors.blueAccent, size: 8); pController.particles = [particle1,particle2,particle3,particle4,particle5,particle6,particle7,particle8,]; }
當(dāng)然畫板那里也需要修改為循環(huán)繪制粒子。
效果:
可以看到木星引力最強(qiáng),最先停止,水星和火星的引力基本一致最弱,最后靜止。
總結(jié)
粒子運(yùn)動可以說是一種特殊的動畫,通過特定的物理運(yùn)動公式可以達(dá)到我們想要的運(yùn)動軌跡,從而實現(xiàn)一些花里胡哨的動畫效果,這里只是展示里其中的一種公式,例如一些拋物線運(yùn)動、隨機(jī)運(yùn)動有興趣的小伙伴可以試試,關(guān)鍵是修改粒子控制器的update
方法,改變粒子的運(yùn)動屬性即可。
原文鏈接:https://juejin.cn/post/7158850965116813320
相關(guān)推薦
- 2021-12-08 .net?core?api接口JWT方式認(rèn)證Token_實用技巧
- 2022-10-30 C++解析obj模型文件方法介紹_C 語言
- 2022-12-13 C++?Boost?Pool超詳細(xì)講解_C 語言
- 2022-08-26 docker搭建memcached的詳細(xì)步驟_docker
- 2022-12-12 C語言中單鏈表(不帶頭結(jié)點)基本操作的實現(xiàn)詳解_C 語言
- 2022-10-16 Flask快速實現(xiàn)分頁效果示例_python
- 2022-07-19 C++調(diào)用Matplotlibcpp進(jìn)行畫圖
- 2022-05-18 ASP.NET?MVC授權(quán)過濾器用法_實用技巧
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- 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)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支