Flutter第2天--Animation动画+粒子运动

Flutter七日游第二天----2018-12-17----天气晴朗

零前言:

昨天讲了一下Flutter的基础项目,介绍了一下Dart语言,简单的用Canvas绘个图
本来打算今天把内置控件看一下的,不过既然昨天把图都画了,今天不玩动画岂不可惜
今天主要是把动画理一下,顺便把Android-java粒子运动复刻到Flutter-Dart里
最后会实现一个粒子时钟,Java版详见:Android原生绘图之炫酷倒计时
所以这篇会挺好玩的,瓜子,饮料,花生米备好,开始看吧

先挑几个图镇楼吧

运动盒 粉碎球
星与芒 星扩动

一、入门级动画:五角星的长大


1.照葫芦画瓢

按照昨天的Flutter初始项目,我们来自己写一个
本人是喜欢分包的,Javaer的优良习惯。至少逻辑清晰,分工明确,我创建了一个pager包
主页面内容用AnimaPage,虽然暂时还不知道StatefulWidget是什么,反正按照套路出牌就行了
仿照初始项目的套路写,这里绘图区自定义AnimaView,打算对五角星的外接圆半径R进行动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:toly/view/anima_view.dart';

class AnimaPage extends StatefulWidget {
@override
_AnimaPageState createState() => _AnimaPageState();
}

class _AnimaPageState extends State<AnimaPage>{
double _R = 25;//五角星的外接圆半径

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("张风捷特烈"),
),
body: CustomPaint(
painter: AnimaView(context, _R),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

2.AnimaView的实现:

n角星的路径第一天已经封装好了,不会的可以去看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:toly/helper/common_path.dart';
import 'package:toly/helper/help_view.dart';

class AnimaView extends CustomPainter {
Paint mPaint;
BuildContext context;
double _R;

AnimaView(this.context, double r) {
mPaint = new Paint();
mPaint.color = Colors.deepOrange;
_R = r;
}

@override
void paint(Canvas canvas, Size size) {
var winSize = MediaQuery.of(context).size;
drawGrid(canvas, winSize);
drawCoo(canvas, new Size(160, 320), winSize);
canvas.translate(160, 320);
canvas.drawPath(nStarPath(5, _R, 50), mPaint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

3.让数据动起来

现在万事俱备,只欠东风把R的值吹动就行了,下面有请Animation登场
vsync需要with SingleTickerProviderStateMixi
Tween:补间动画—-这里即:在 25.0, ~ 150.0之间在2000ms之内均匀变化
(PS:由于程序运行情况不同,并非绝对均匀,但整体上是均匀的)
..:是级联运算,相当于再使用此对象,这里..也就代表animation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class AnimaPage extends StatefulWidget {
@override
_AnimaPageState createState() => _AnimaPageState();
}

class _AnimaPageState extends State<AnimaPage>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
double _R = 25;

@override
void initState() {
super.initState();
// 创建 AnimationController 对象
//|----vsync时会防止屏幕外动画(动画的UI不在当前屏幕时)消耗不必要的资源
controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = Tween(begin: 25.0, end: 150.0).animate(controller)
..addListener(() {
setState(() {
_R = animation.value;
});
});
}

@override
void dispose() {
super.dispose();
controller.dispose(); // 资源释放
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("张风捷特烈"),
),
body: CustomPaint(
painter: AnimaView(context, _R),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
controller.forward(); //执行动画
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

这样最简单的动画就动起来了


4.来理一理思路

把源码翻一翻,感觉整个动画体系也不是非常复杂
套路就是:Animatable用animate方法,把一个Animation包裹一下,形成一个更厉害的Animation
至于他们的n个儿子,也就是对数据的处理不同,产生的效果不同罢了,套路知道了,一切好办


可以看出api并没有想象中的那么多,所以别怕


二、入门级动画:五角星的绽放

前面用了补间动画Tween,而且只动了一下,下面带来连续运动的不均匀动画

匀速往复动 自定义曲线 bounceInOut

1.往复运动
1.1:运动状态

你可以想象成一个人在一个范围数字跑道上跑步:

1
2
3
4
5
6
7
8
9
10
enum AnimationStatus {
/// The animation is stopped at the beginning
dismissed,//在正在开始时停止了
/// The animation is running from beginning to end
forward,//运动中
/// The animation is running backwards, from end to beginning
reverse,//跑到终点,再跑回来的时候
/// The animation is stopped at the end
completed,//跑到终点是
}

1.2:状态的监听

addStatusListener:可以监听当前运动状态:只要让它跑完了,再往回跑就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
animation = Tween(begin: 25.0, end: 150.0).animate(controller)
..addListener(() {
setState(() {
_R = animation.value;
});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});

2.变速跑

就像运动员变速跑一样,感觉就像游戏里加buff,本来是匀速的Animation
给价格变速的buff就变速起来了,加buff的类就是Animatable,它的子类有一个CurveTween

1
2
animation = Tween(begin: 25.0, end: 150.0).animate(
CurveTween(curve: Curves.bounceInOut).animate(controller))

就这么简单,Curves里有几个内置的变速器,给原来的animation装上就行了


3.自定义变速曲线
3.1:追踪一下源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
---->[CurveTween]---------控件只有一参Curve------------------------
class CurveTween extends Animatable<double> {
/// Creates a curve tween.
///
/// The [curve] argument must not be null.
CurveTween({ @required this.curve })
: assert(curve != null);

/// The curve to use when transforming the value of the animation.
Curve curve;

---->[Curve]--------抽象的,找儿子去--------------------
@immutable
abstract class Curve {

---->[Curve]--------四参构造的曲线,整合我意--------------------
class Cubic extends Curve {
/// Creates a cubic curve.
///
/// Rather than creating a new instance, consider using one of the common
/// cubic curves in [Curves].
///
/// The [a], [b], [c], and [d] arguments must not be null.
const Cubic(this.a, this.b, this.c, this.d)

3.2:Chrome小工具

作为一名前端业余爱好者,Chrome里有个小东西很有用,
曲线生成,自带预览,简直无心插柳柳成荫。(记得掘金的头像可以转,有translate属性)


3.3:使用:
1
2
animation = Tween(begin: 25.0, end: 150.0).animate(
CurveTween(curve: Cubic(0.96, 0.13, 0.1, 1.2)).animate(controller))

Ok,基本上就这样,你get了吗?


三、初级动画:太阳的诞生

红太阳 星与阳
1.红太阳:整型int 动画

套路学会了,这些动态改变一下n角星的尖角数,看看效果

1.1:AnimaPage里定义尖角数动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Animation<int> numAnima;//n角星的尖角数动画
int _num = 5;//n角星的尖角数动画

controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);

numAnima = IntTween(begin: 5, end: 220).animate(controller)
..addListener(() {
setState(() {
_num = numAnima.value;//设置属性,刷新界面
});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});

1.2:AnimaView增加入参
1
2
3
4
5
6
7
int _num;
AnimaView(this.context, {double R, int num, Color color}) {
_num = num;
}

//绘制时使用_num即可
canvas.drawPath(nStarPath(_num, 100, 50), mPaint);

2.星与阳:颜色动画

ColorTween相当于添加颜色改变的buff,入参的自己加吧,和上面一样,给画笔填色就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
colorAnima =
ColorTween(begin: Colors.yellow, end: Colors.red).animate(controller)
..addListener(() {
setState(() {
_color = colorAnima.value;
});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});

3.其他效果

自己玩玩吧,随便改些属性

星与芒 星扩动

星与芒:固定五角星内接圆半径,外接圆半径变大,角数变多,颜色变红
星扩动:五角星内接圆半径为外接圆一半,外接圆半径变大,角数变多,颜色变红

好了,预热完了,下面进入正题

四、粒子运动

无论什么语言只有能模拟时间流就可以有粒子动画
粒子动画的基础在Android原生绘图之让你了解View的运动里讲的很详细
思想对于所有语言都是通用的,不仅限于java,有兴趣的可以详细了解下

1.废话不多说,来个运动盒再说

运动盒就是小球在盒子里不断弹跳的动画,就像这样

1.1:新建文件:run_ball_view.dart----->RunBallView + Ball

先把小球的实体类写一下

1
2
3
4
5
6
7
8
9
10
11
12
class Ball {
double aX; //加速度
double aY; //加速度Y
double vX; //速度X
double vY; //速度Y
double x; //点位X
double y; //点位Y
Color color; //颜色
double r;//小球半径

Ball({this.x, this.y, this.color, this.r, this.aX, this.aY, this.vX, this.vY});
}

1.2:画板准备好
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class RunBallView extends CustomPainter {
Paint mPaint;
BuildContext context;
Ball _ball;
Rect _limit;

RunBallView(this.context, Ball ball, Rect limit) {
mPaint = new Paint();
_ball = ball;
_limit = limit;
}

@override
void paint(Canvas canvas, Size size) {
var winSize = MediaQuery.of(context).size;
canvas.translate(160, 320);
mPaint.color = Color.fromARGB(148, 198, 246, 248);
canvas.drawRect(_limit, mPaint);

canvas.save();
drawBall(canvas, _ball);
canvas.restore();
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
//绘制小球
void drawBall(Canvas canvas, Ball ball) {
mPaint.color = ball.color;
canvas.drawCircle(Offset(ball.x, ball.y), ball.r, mPaint);
}
}

1.3:认识一下矩形对象Rect

这是我们需要的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var limit = Rect.fromLTRB(-140, -100, 140, 100);

print("width:${limit.width}");
print("height:${limit.height}");
print("left:${limit.left}");
print("top:${limit.top}");
print("right:${limit.right}");
print("bottom:${limit.bottom}");

I/flutter (28755): width:280.0
I/flutter (28755): height:200.0
I/flutter (28755): left:-140.0
I/flutter (28755): top:-100.0
I/flutter (28755): right:140.0
I/flutter (28755): bottom:100.0

1.4:新建文件:run_ball_pager.dart---->RunBallPage

这里只需要一个时间流,用AnimationController一个人就够了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class RunBallPage extends StatefulWidget {
@override
_RunBallPageState createState() => _RunBallPageState();
}

class _RunBallPageState extends State<RunBallPage>
with SingleTickerProviderStateMixin {
AnimationController controller;
Ball _ball;
var _limit = Rect.fromLTRB(-140, -100, 140, 100);//矩形边界

@override
void initState() {
super.initState();
//初始化小球
_ball = Ball(x: 0, y: 0, color: Colors.blue, r: 10, aX: 0, aY: 0, vX: 0, vY: 2);

// 创建 AnimationController 对象
//|----vsync时会防止屏幕外动画(动画的UI不在当前屏幕时)消耗不必要的资源
controller = AnimationController(
duration: const Duration(milliseconds: 200000), vsync: this);

controller.addListener(() {
updateBall();//更新小球

setState(() {});
});

controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
}

@override
void dispose() {
super.dispose();
controller.dispose(); // 资源释放
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("张风捷特烈"),
),
body: CustomPaint(
painter: RunBallView(context, _ball,_limit),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
controller.forward(); //执行动画
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}

//更新小球
void updateBall() {}
}

这样静态小球就完成了


1.5:更新新小球位置,渲染视图:RunBallPage#updateBall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//更新小球位置
void updateBall() {
//运动学公式
_ball.x += _ball.vX;
_ball.y += _ball.vY;
_ball.vX += _ball.aX;
_ball.vY += _ball.aY;
//限定下边界
if (_ball.y > _limit.bottom - _ball.r) {
_ball.y = _limit.bottom - _ball.r;
_ball.vY = -_ball.vY;
_ball.color=randomRGB();//碰撞后随机色
}
//限定上边界
if (_ball.y < _limit.top + _ball.r) {
_ball.y = _limit.top + _ball.r;
_ball.vY = -_ball.vY;
_ball.color=randomRGB();//碰撞后随机色
}
}

1.6:运动盒的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//初始时附加信息
_ball = Ball(x: 0, y: 0, color: Colors.blue, r: 10, aX: 0, aY: 0.1, vX: 2, vY: -2);

//更新小球位置
void updateBall() {
//运动学公式
_ball.x += _ball.vX;
_ball.y += _ball.vY;
_ball.vX += _ball.aX;
_ball.vY += _ball.aY;
//限定下边界
if (_ball.y > _limit.bottom - _ball.r) {
_ball.y = _limit.bottom - _ball.r;
_ball.vY = -_ball.vY;
_ball.color=randomRGB();//碰撞后随机色
}
//限定上边界
if (_ball.y < _limit.top + _ball.r) {
_ball.y = _limit.top + _ball.r;
_ball.vY = -_ball.vY;
_ball.color=randomRGB();//碰撞后随机色
}

//限定左边界
if (_ball.x < _limit.left + _ball.r) {
_ball.x = _limit.left + _ball.r;
_ball.vX = -_ball.vX;
_ball.color=randomRGB();//碰撞后随机色
}

//限定右边界
if (_ball.x > _limit.right - _ball.r) {
_ball.x = _limit.right - _ball.r;
_ball.vX= -_ball.vX;
_ball.color=randomRGB();//碰撞后随机色
}
}

2.粒子的运动

思路就是:用List把球装一下,碰撞的时候,创建一个方向相反,半径减半的球,加入集合
并将当前的球半径减半,效果挺不错的,实现起来也不麻烦。Android-java版可见


2.1:改动:RunBallPage

半径小于0.3就移除,为了不让小球无限增加,小于0.3基本上也就卡不见了
当然你也可以自定义移除的时机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
var _balls = List<Ball>();//将_ball换成集合

var ball = Ball(x: 0, y: 0, color: Colors.blue, r: 40, aX: 0.05, aY: 0.1, vX: 3, vY: -3);
_balls.add(ball);//添加一个

//更新方法
for (int i = 0; i < _balls.length; i++) {
var ball = _balls[i];
if (ball.r < 0.3) {
//半径小于0.3就移除
_balls.removeAt(i);
}
//运动学公式
ball.x += ball.vX;
ball.y += ball.vY;
ball.vX += ball.aX;
ball.vY += ball.aY;
//限定下边界
if (ball.y > _limit.bottom) {
var newBall = Ball.fromBall(ball);
newBall.r = newBall.r / 2;
newBall.vX = -newBall.vX;
newBall.vY = -newBall.vY;
_balls.add(newBall);
ball.r = ball.r / 2;

ball.y = _limit.bottom;
ball.vY = -ball.vY;
ball.color = randomRGB(); //碰撞后随机色
}
//限定上边界
if (ball.y < _limit.top) {
ball.y = _limit.top;
ball.vY = -ball.vY;
ball.color = randomRGB(); //碰撞后随机色
}

//限定左边界
if (ball.x < _limit.left) {
ball.x = _limit.left;
ball.vX = -ball.vX;
ball.color = randomRGB(); //碰撞后随机色
}

//限定右边界
if (ball.x > _limit.right) {
var newBall = Ball.fromBall(ball);
newBall.r = newBall.r / 2;
newBall.vX = -newBall.vX;
newBall.vY = -newBall.vY;
_balls.add(newBall);
ball.r = ball.r / 2;

ball.x = _limit.right;
ball.vX = -ball.vX;
ball.color = randomRGB(); //碰撞后随机色
}
}
}

//传入画布也变成小球集合
body: CustomPaint(
painter: RunBallView(context, _balls, _limit),
),

2.2:绘制小球时:RunBallView

把小球都绘制出来就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//复制一个小球
Ball.fromBall(Ball ball) {
this.x = ball.x;
this.y = ball.y;
this.color = ball.color;
this.r = ball.r;
this.aX = ball.aX;
this.aY = ball.aY;
this.vX = ball.vX;
this.vY = ball.vY;
}

//------paint方法中-----------
_balls.forEach((ball) {
drawBall(canvas, ball);
});

这样就完成了,是不是没有想象中的那么复杂


五、粒子时钟

这里就不详细分析,这里的Java版已经分析的很细致了,直接上代码(基本上是Java的翻译版)
这个效果新建了一个页面来做,digit三维数组Dart版附在文尾

1.渲染数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 渲染数字
* @param num 要显示的数字
* @param canvas 画布
*/
void renderDigit(int num, Canvas canvas) {
if (num > 10) {
return;
}
for (int i = 0; i < digit[num].length; i++) {
for (int j = 0; j < digit[num][j].length; j++) {
if (digit[num][i][j] == 1) {
canvas.save();
double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标
double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标
canvas.translate(rX, rY);
mPaint.color = Colors.blue;
canvas.drawPath(mStarPath, mPaint);
canvas.restore();
}
}
}
}

2.画布中绘制1994
1
2
3
4
5
6
7
8
9
canvas.save();
renderDigit(1, canvas);
canvas.translate(80, 0);
renderDigit(9, canvas);
canvas.translate(80, 0);
renderDigit(9, canvas);
canvas.translate(80, 0);
renderDigit(4, canvas);
canvas.restore();

3.绘制时间

3.1:时间的简单获取
1
2
3
4
5
6
7
8
DateTime now = new DateTime.now();
var hour = now.hour;
var second = now.second;
var minute = now.minute;

print("hour$hour");//15
print("second$second");//57
print("minute$minute");//27

3.2:绘制时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//时
renderDigit(_now.hour ~/ 10, canvas);
canvas.translate(19 * _radius, 0);
renderDigit(_now.hour % 10, canvas);
//:
canvas.translate(19 * _radius, 0);
renderDigit(10, canvas);
//分
canvas.translate(11 * _radius, 0);
renderDigit(_now.minute ~/ 10, canvas);
canvas.translate(19 * _radius, 0);
renderDigit(_now.minute % 10, canvas);
//:
canvas.translate(18 * _radius, 0);
renderDigit(10, canvas);
//秒
canvas.translate(11 * _radius, 0);
renderDigit(_now.second ~/ 10, canvas);
canvas.translate(19 * _radius, 0);
renderDigit(_now.second % 10, canvas);

3.3:用Animation动起来


4.加小球

方法基本上是Java版改些的,这里不分析了,可以看Java版的分析,基本上一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//在类的外部定义全局的变量
var currTime = new DateTime.now();
var tagOfBall = new DateTime.now().millisecondsSinceEpoch;
var _balls = new List<Ball>();

//构造方法中刷新小球
ClockView(this.context) {
mPaint = new Paint();
mStarPath = nStarPath(5, _radius, _radius / 2);
_now = new DateTime.now();
addBallsChanged();
updateBalls();
}


/**
* 添加倒计时中改动的点---变动加球
*/
void addBallsChanged() {
var now = new DateTime.now();
if (currTime.second != now.second) {
//判断当前时间是否改变,再将点位放到集合中
if ((currTime.hour ~/ 10) != (now.hour ~/ 10)) {
addBalls((-17 * 5 - 11 * 2) * _radius.toInt(), currTime.hour ~/ 10);
}
if ((currTime.hour % 10) != (now.hour % 10)) {
addBalls((-17 * 4 - 11 * 2) * _radius.toInt(), currTime.hour % 10);
}
if ((currTime.minute ~/ 10) != (now.minute ~/ 10)) {
addBalls((-18 * 3 - 11) * _radius.toInt(), currTime.minute ~/ 10);
}
if ((currTime.minute % 10) != (now.minute % 10)) {
addBalls((-18 * 2 - 11) * _radius.toInt(), currTime.minute % 10);
}
if ((currTime.second ~/ 10) != (now.second ~/ 10)) {
addBalls(-18 * _radius.toInt(), currTime.second ~/ 10);
}
if ((currTime.second % 10) != (now.second % 10)) {
addBalls(0, currTime.second % 10);
currTime = now;
}
}
}

//添加小球
addBalls(int offsetX, int num) {
Random random = new Random();
for (int i = 0; i < digit[num].length; i++) {
for (int j = 0; j < digit[num][i].length; j++) {
if (digit[num][i][j] == 1) {
Ball ball = new Ball();
ball.aY = 0.1;
ball.vX = pow(-1, random.nextInt(1000)) * 6 * random.nextDouble();
ball.vY = 4 * random.nextDouble();
ball.x =
offsetX + j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标
ball.y = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标
ball.color = randomRGB();
ball.r = _radius;
_balls.add(ball);
}
}
}
}

/**
* 更新所有球的位置---让球运动
* 并且越界移除
*/
void updateBalls() {
double maxX = 400; //限定x范围大值

for (Ball ball in _balls) {
ball.x += ball.vX; //x=xo+v*t-----t=1
ball.y += ball.vY;
ball.y += ball.aY; //v=vo+a*t-----t=1

if (ball.y >= 160) {
//超过Y底线,反弹
ball.y = 160;
ball.vY = -ball.vY * 0.99;
}

if (ball.x > maxX) {
//超过X最大值,反弹
ball.x = maxX;
ball.vX = -ball.vX * 0.99;
}
}

//5秒清一次屏
if (new DateTime.now().millisecondsSinceEpoch - tagOfBall > 5000) {
_balls.clear();
tagOfBall = new DateTime.now().millisecondsSinceEpoch;
}
}

好了,今天就这样,内容有点多,现在Dart的语法越用越熟练了


附录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//颜色常量
import 'dart:ui';

const colors = [
Color(0x8833B5E5),
Color(0x880099CC),
Color(0x889933CC),
Color(0x8899CC00),
Color(0x88669900),
Color(0x88FFBB33),
Color(0x88FF8800),
Color(0x88FF4444),
Color(0x88CC0000)
];

const digit = [
[
[0, 0, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 1, 1, 0],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 1, 0, 1, 1, 0],
[0, 0, 1, 1, 1, 0, 0]
], //0

[
[0, 0, 0, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[1, 1, 1, 1, 1, 1, 1]
], //1
[
[0, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 1, 1, 1, 1, 1]
], //2
[
[1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 1, 1, 1, 1, 0]
], //3

[
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 0],
[0, 0, 1, 1, 1, 1, 0],
[0, 1, 1, 0, 1, 1, 0],
[1, 1, 0, 0, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 1, 1]
], //4
[
[1, 1, 1, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 1, 1, 1, 1, 0]
], //5
[
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0],
[1, 1, 0, 1, 1, 1, 0],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 1, 1, 1, 1, 0]
], //6
[
[1, 1, 1, 1, 1, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 0]
], //7
[
[0, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 1, 1, 1, 1, 0]
], //8
[
[0, 1, 1, 1, 1, 1, 0],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 1, 1],
[0, 1, 1, 1, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 0],
[0, 1, 1, 0, 0, 0, 0]
], //9
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
] //:
];
张风捷特烈 wechat
-------------本文结束感谢您的阅读 @张风捷特烈-------------
0%