Android?Flutter實(shí)現(xiàn)自由落體彈跳動(dòng)畫(huà)效果
粒子運(yùn)動(dòng)概念
粒子運(yùn)動(dòng)是將對(duì)象按照一定物理公式進(jìn)行的自定義軌跡運(yùn)動(dòng),與普通動(dòng)畫(huà)不同的是,它沒(méi)有強(qiáng)制性的動(dòng)畫(huà)開(kāi)始到結(jié)束的時(shí)間概念,因?yàn)榱W拥倪\(yùn)動(dòng)開(kāi)始到結(jié)束的時(shí)間并不是固定的,而是由具體場(chǎng)景的物理運(yùn)動(dòng)公式來(lái)決定的,什么時(shí)候結(jié)束由你來(lái)定,例如:小球自由落體彈跳動(dòng)畫(huà)松開(kāi)小球開(kāi)始到地面停止的時(shí)間就跟距離地面初始高度有關(guān),初始高度越高,動(dòng)畫(huà)時(shí)間越長(zhǎng),反之依然,所以,粒子運(yùn)動(dòng)可以說(shuō)是符合物理公式并持續(xù)不斷的動(dòng)畫(huà)。
粒子運(yùn)動(dòng)特點(diǎn):符合物理運(yùn)動(dòng)公式、持續(xù)不斷運(yùn)動(dòng)。
如何保持持續(xù)運(yùn)動(dòng)
我們可以通過(guò)動(dòng)畫(huà)控制器AnimationController調(diào)用repeat();方法開(kāi)啟無(wú)限循環(huán)動(dòng)畫(huà)來(lái)實(shí)現(xiàn),這里時(shí)間設(shè)置多少都行,因?yàn)槲覀儾挥盟?,而是?code>addListener()這個(gè)方法來(lái)觸發(fā)小球運(yùn)動(dòng),這個(gè)方法可以理解為粒子運(yùn)動(dòng)的刷新率,通常1秒觸發(fā)回調(diào)60次,通過(guò)這個(gè)回調(diào)我們就可以持續(xù)不斷的驅(qū)使小球改運(yùn)動(dòng)。
late AnimationController _controller;
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
) ..addListener((){
// 通常這個(gè)回調(diào)會(huì)一秒回調(diào)60次 也就是我們平常的60hz屏幕刷新率。
})..repeat();
創(chuàng)建粒子對(duì)象
理解了上方的信息,接下來(lái)我們首先創(chuàng)建一個(gè)粒子對(duì)象,粒子對(duì)象包含粒子運(yùn)動(dòng)所需速度、加速度、位移等信息。
代碼:
// 粒子對(duì)象
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)建粒子控制器
有了粒子對(duì)象,接下來(lái)創(chuàng)建粒子控制器,混入ChangeNotifier通過(guò)改變粒子屬性通知畫(huà)板刷新,這里通過(guò)update方法改變小球的運(yùn)動(dòng)軌跡。
我們知道自由落體彈跳,由于地心引力和能量守恒,在沒(méi)有外力的加持下,小球落地彈起的過(guò)程是一個(gè)加速 - 彈起 - 減速 - 速度為0 - 再加速...的過(guò)程,最終小球相對(duì)地面達(dá)到靜止?fàn)顟B(tài),那么我們假設(shè)小球垂直自由落體彈跳,由于能量的損失,小球彈起速度為下落撞擊地面速度的4/5,那么隨著時(shí)間的推移,小球的速度就會(huì)越來(lái)越慢,直到靜止。
代碼:
// 粒子控制器
class ParticleController with ChangeNotifier {
// 粒子
late Particle p;
// 粒子運(yùn)動(dòng)區(qū)域
Size? size;
ParticleController();
void update() {
// 此方法一秒刷新60次
// 距離= 時(shí)間 * 速度。
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),動(dòng)畫(huà)結(jié)束 恢復(fù)初始高度,以及最大高度
p.y = p.initY;
p.maxY = size!.height;
}
notifyListeners();
}
}初始化粒子
創(chuàng)建粒子控制器,初始化粒子,設(shè)置粒子初始位移、初始速度,加速度等信息,并將粒子控制器傳給畫(huà)板。
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)建畫(huà)板
創(chuàng)建畫(huà)板,繪制小球和輔助區(qū)域,小球圓心為粒子位移的距離。
class _BallMove extends CustomPainter {
//
final ParticleController controller;
Paint ballPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
// 實(shí)現(xiàn)super方法 實(shí)現(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)動(dòng)區(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;
}
}效果:

這樣就實(shí)現(xiàn)了小球自由落體彈跳效果,當(dāng)然這只是理想的狀態(tài)下的自由落體,真實(shí)狀態(tài)下有很多因素的影響,像空氣阻力、風(fēng)等因素。上面只是實(shí)現(xiàn)了一個(gè)粒子的自由落體,加速度為地球重力加速度,多粒子運(yùn)動(dòng)原理一樣。
多粒子實(shí)現(xiàn)八大行星加速度自由落體彈跳
修改粒子控制器增加粒子集合,實(shí)現(xiàn)多粒子運(yùn)動(dòng),
// 粒子集合
List<Particle> particles = [];
void update() {
// 循環(huán)粒子集合
particles.forEach(doUpdate);
notifyListeners();
}
void doUpdate(Particle p) {
// 一秒刷新60次
// 距離= 時(shí)間 * 速度。
// 自由落體 速度不斷加快,地球加速度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à)板那里也需要修改為循環(huán)繪制粒子。
效果:

可以看到木星引力最強(qiáng),最先停止,水星和火星的引力基本一致最弱,最后靜止。
總結(jié)
粒子運(yùn)動(dòng)可以說(shuō)是一種特殊的動(dòng)畫(huà),通過(guò)特定的物理運(yùn)動(dòng)公式可以達(dá)到我們想要的運(yùn)動(dòng)軌跡,從而實(shí)現(xiàn)一些花里胡哨的動(dòng)畫(huà)效果,這里只是展示里其中的一種公式,例如一些拋物線(xiàn)運(yùn)動(dòng)、隨機(jī)運(yùn)動(dòng)有興趣的小伙伴可以試試,關(guān)鍵是修改粒子控制器的update方法,改變粒子的運(yùn)動(dòng)屬性即可。
以上就是Android Flutter實(shí)現(xiàn)自由落體彈跳動(dòng)畫(huà)效果的詳細(xì)內(nèi)容,更多關(guān)于Android Flutter自由落體彈跳動(dòng)畫(huà)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android 1.5 1.6 2.0 2.1 2.2 的區(qū)別詳解
本篇文章是對(duì)Android 1.5 1.6 2.0 2.1 2.2 版本之間的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
Android實(shí)現(xiàn)點(diǎn)擊AlertDialog上按鈕時(shí)不關(guān)閉對(duì)話(huà)框的方法
這篇文章主要介紹了Android實(shí)現(xiàn)點(diǎn)擊AlertDialog上按鈕時(shí)不關(guān)閉對(duì)話(huà)框的方法,涉及設(shè)置監(jiān)聽(tīng)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02
Android應(yīng)用中炫酷的橫向和環(huán)形進(jìn)度條的實(shí)例分享
這篇文章主要介紹了Android應(yīng)用中炫酷的橫向和圓形進(jìn)度條的實(shí)例分享,文中利用了一些GitHub上的插件進(jìn)行改寫(xiě),也是一片很好的二次開(kāi)發(fā)教學(xué),需要的朋友可以參考下2016-04-04
Android中的Fragment類(lèi)使用進(jìn)階
這篇文章主要介紹了Android中的Fragment類(lèi)使用進(jìn)階,重點(diǎn)講解了Fragment與Activity的交互以及Fragment間的數(shù)據(jù)傳遞,需要的朋友可以參考下2016-04-04
android基礎(chǔ)總結(jié)篇之二:Activity的四種launchMode
這篇文章主要介紹了android基礎(chǔ)總結(jié)篇之二:Activity的四種launchMode,有需要的可以了解一下。2016-11-11
Android 手機(jī)防止休眠的兩種實(shí)現(xiàn)方法
這篇文章主要介紹了Android 手機(jī)防止休眠方法的相關(guān)資料,一種是在Manifest.xml文件里面聲明,另外一種方法是在代碼里面修改LayoutParams的標(biāo)志位,需要的朋友可以參考下2017-08-08
Android傳遞Bitmap對(duì)象在兩個(gè)Activity之間
這篇文章主要介紹了Android傳遞Bitmap對(duì)象在兩個(gè)Activity之間的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android自定義view實(shí)現(xiàn)圓形進(jìn)度條效果
這篇文章主要介紹了Android自定義view實(shí)現(xiàn)圓形進(jìn)度條效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05

