还是老套路,先来看看今天要完成的效果:
首页的二维码是我的公众号,随后我会上传到公众号上
今天要完成的是最后的下雪动画~
先来分析一波:
先给大家介绍怎么画一个圆
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black,
child: CustomPaint(
painter: MyCustomPainter(),
//占满屏幕
size: MediaQuery.of(context).size,
),
);
}
class MyCustomPainter extends CustomPainter {
//创建画笔
Paint _paint = new Paint()
..isAntiAlias = true
..color = Colors.white;
@override
void paint(Canvas canvas, Size size) {
canvas.drawCircle(Offset(100,100), 50, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
//刷新
return true;
}
}
可以参考文档Flutter 轮子之气泡登录页来了解这部分代码
创建雪花Bean类
class SnowBean {
//位置
Offset postion = Offset(100,100);
//半径
double radius = 50;
}
class MyCustomPainter extends CustomPainter {
//创建画笔
Paint _paint = new Paint()
..isAntiAlias = true
..color = Colors.white;
@override
void paint(Canvas canvas, Size size) {
SnowBean snowBean = new SnowBean();
canvas.drawCircle(snowBean.postion, snowBean.radius, _paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
.... }
}
List<SnowBean> _list = [];
@override
void initState() {
// TODO: implement initState
super.initState();
Future.delayed(Duration(seconds: 0), () {
initData();
});
}
void initData() {
for (int i = 0; i <= 200; i++) {
SnowBean snowBean = new SnowBean();
//MediaQuery.of(context).size.width是屏幕的宽
//MediaQuery.of(context).size.height是屏幕的高
double dx = _random.nextDouble() * MediaQuery.of(context).size.width;
double dy = _random.nextDouble() * MediaQuery.of(context).size.height;
snowBean.postion = Offset(dx, dy);
snowBean.radius = _random.nextDouble() + 1;
_list.add(snowBean);
}
}
class MyCustomPainter extends CustomPainter {
//创建画笔
.....
List<SnowBean> list;
MyCustomPainter(this.list);
@override
void paint(Canvas canvas, Size size) {
list.forEach((element) {
canvas.drawCircle(element.postion, element.radius, _paint);
});
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
.... }
}
这段代码非常简单,就是在initState()初始化的时候,使用异步创建200个随机大小,随机位置的圆,并吧这个list传递给MyCustomPainter(),然他绘制出每一个圆
AnimationController _animationController;
@override
void initState() {
// TODO: implement initState
super.initState();
//创建动画,执行为1秒
_animationController =
new AnimationController(vsync: this, duration: Duration(seconds: 10));
//动画监听器
_animationController.addListener(() {
setState(() {
});
});
//重复执行气泡动画
_animationController.repeat();
}
这段代码参考Flutter AnimatedWidget,AnimatedBuilder动画(2.4)
接下来重新写一下MyCustomPainter ()方法
class MyCustomPainter extends CustomPainter {
List<SnowBean> list;
Random _random;
MyCustomPainter(this.list, this._random);
Paint _paint = new Paint()
..isAntiAlias = true
..color = Colors.white;
@override
void paint(Canvas canvas, Size size) {
list.forEach((element) {
double dy = element.postion.dy;
double dx = element.postion.dx;
/**
* 若Y轴雪花移动到屏幕以外,则让它回到起始位置
*/
if (dy >= size.height) {
dy = 0;
}
/**
* 若X轴雪花移动到屏幕以外,则让它回到起始位置
*/
if (dx >= size.width) {
dx = 0;
}
element.postion =
Offset(dx + _random.nextDouble(), dy + _random.nextDouble() + element.speed + 3);
});
list.forEach((element) {
canvas.drawCircle(element.postion, element.radius, _paint);
});
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
SnowBean添加移动的速度:
class SnowBean {
//位置
Offset postion = Offset(100, 100);
//半径
double radius = 50;
//移动速度
double speed;
}
重新初始化:
void initData() {
for (int i = 0; i <= 200; i++) {
SnowBean snowBean = new SnowBean();
//_random.nextDouble()是0-1随机的小数
double dx = _random.nextDouble() * MediaQuery.of(context).size.width;
double dy = _random.nextDouble() * MediaQuery.of(context).size.height;
snowBean.postion = Offset(dx, dy);
snowBean.radius = _random.nextDouble() + 1;
snowBean.speed = _random.nextDouble() + 5;
_list.add(snowBean);
}
}
这段代码我相信大家看到大部分还是可以看懂的;
给大家解释一下这段代码:
/**
* 若Y轴雪花移动到屏幕以外,则让它回到起始位置
*/
if (dy >= size.height) {
dy = 0;
}
/**
* 若X轴雪花移动到屏幕以外,则让它回到起始位置
*/
if (dx >= size.width) {
dx = 0;
}
这段代码的意思是,当dx或者dy移动出屏幕时,让他重新绘制
element.postion =
Offset(dx + _random.nextDouble(),
dy + element.speed );
来看看效果:
可能有同学还是会疑惑,让动画重复执行了,可是为什么雪花会动呢?
注意看这个setState(){}方法
其实啊:这里的setState(){}这个方法,一直在执行sholdRepaint方法()
先会判断这里的返回值,如果是true则会一直刷新paint()方法,paint()中的操作简单理解就是让dx左右移动,dy一直向下移动,当移动到屏幕外时,则让它回到起始位置,则达到一直’下雪’的效果,如果返回的是false,则只执行一次
完整代码
猜你喜欢:
Flutter 轮子之气泡登录页(1)
Android 轮子之下雪效果
原创不易,您的点赞就是对我最大的支持,留下您的点赞吧~