我正在参加「启航方案」。
粒子运动概念
粒子运动是将目标依照一定物理公式进行的自定义轨道运动,与普通动画不同的是,它没有强制性的动画开始到完毕的时刻概念,因为粒子的运动开始到完毕的时刻并不是固定的,而是由具体场景的物理运动公式来决定的,什么时候完毕由你来定,例如:小球自由落体弹跳动画松开小球开始到地上中止的时刻就跟间隔地上初始高度有关,初始高度越高,动画时刻越长,反之依然,所以,粒子运动能够说是契合物理公式并持续不断的动画。
- 粒子运动特点:契合物理运动公式、持续不断运动。
如何保持持续运动 ?
咱们能够经过动画控制器AnimationController
调用repeat();
办法开启无限循环动画来完成,这儿时刻设置多少都行,因为咱们不用它,而是用addListener()
这个办法来触发小球运动,这个办法能够理解为粒子运动的改写率,一般1
秒触发回调60
次,经过这个回调咱们就能够持续不断的驱使小球改运动。
late AnimationController _controller;
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
) ..addListener((){
// 一般这个回调会一秒回调60次 也便是咱们平常的60hz屏幕改写率。
})..repeat();
创立粒子目标
理解了上方的信息,接下来咱们首先创立一个粒子目标,粒子目标包含粒子运动所需速度、加速度、位移等信息。
代码:
// 粒子目标
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,
});
}
创立粒子控制器
有了粒子目标,接下来创立粒子控制器,混入ChangeNotifier
经过改变粒子属性告诉画板改写,这儿经过update
办法改变小球的运动轨道。
咱们知道自由落体弹跳,因为地心引力和能量守恒,在没有外力的加持下,小球落地弹起的过程是一个加速 - 弹起 - 减速 - 速度为0 - 再加速...
的过程,终究小球相对地上到达停止状况,那么咱们假定小球笔直自由落体弹跳,因为能量的丢失,小球弹起速度为下落撞击地上速度的4/5
,那么跟着时刻的推移,小球的速度就会越来越慢,直到停止。
代码:
// 粒子控制器
class ParticleController with ChangeNotifier {
// 粒子
late Particle p;
// 粒子运动区域
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;
// 假定能量丢失 反弹速度为下落最大速度的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 咱们以为小球已到达停止状况,动画完毕 恢复初始高度,以及最大高度
p.y = p.initY;
p.maxY = size!.height;
}
notifyListeners();
}
}
初始化粒子
创立粒子控制器,初始化粒子,设置粒子初始位移、初始速度,加速度等信息,并将粒子控制器传给画板。
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触发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),
);
}
}
创立画板
创立画板,制作小球和辅助区域,小球圆心为粒子位移的间隔。
class _BallMove extends CustomPainter {
//
final ParticleController controller;
Paint ballPaint = Paint();
Paint stokePaint = Paint()
..strokeWidth = 0.5
..style = PaintingStyle.stroke;
// 完成super办法 完成改写
_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);
// 小球运动区域
canvas.drawRect(
Rect.fromCenter(
center: Offset.zero,
width: controller.size!.width,
height: controller.size!.height),
stokePaint);
canvas.restore();
// 设置小球色彩
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;
}
}
作用:
这样就完成了小球自由落体弹跳作用,当然这仅仅理想的状况下的自由落体,真实状况下有很多要素的影响,像空气阻力、风等要素。上面仅仅完成了一个粒子的自由落体,加速度为地球重力加速度,多粒子运动原理一样。
多粒子完成八大行星加速度自由落体弹跳
修正粒子控制器增加粒子调集,完成多粒子运动,
// 粒子调集
List<Particle> particles = [];
void update() {
// 循环粒子调集
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;
// 假定能量丢失 反弹速度为弹起的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,];
}
当然画板那里也需求修正为循环制作粒子。
作用:
能够看到木星引力最强,最先中止,水星和火星的引力根本一致最弱,最后停止。
总结
粒子运动能够说是一种特殊的动画,经过特定的物理运动公式能够到达咱们想要的运动轨道,然后完成一些花里胡哨的动画作用,这儿仅仅展示里其间的一种公式,例如一些抛物线运动、随机运动有兴趣的小伙伴能够试试,关键是修正粒子控制器的update
办法,改变粒子的运动属性即可。那么动画分析到此告一段落,期望这三篇文章能够对你在动画的使用上有所帮助~