前言
今天在掘金上看到用Flutter绘制糖豆人动画的相关教程,心血来潮,简单看了一下思路,自己实现了一下
原文地址:https://juejin.cn/post/7088268036804706318
功能展示
实现逻辑
动画
- 定义两个Animation Controller & Animation<double> 分别控制身体和球体的动画控制
- 定义Listener用来更新状态
绘制
- 定义两个颜色Color 分别为身体颜色 和 球体颜色
- 定义画布,构造方法传递参数为身体Animation和球体Animation 以及两个相关颜色
- 绘制身体 以及球和眼睛,传递相关动画Value 进行状态更新
实例
准备工作结束后,我们开始看看代码如何写
绘制
class AnimationPaint extends CustomPainter{
//糖豆人身体颜色
final Color bodyColor;
//豆豆颜色
final Color propColor;
final Animation<double> bodyValue;
final Animation<double> propColorValue;
final Listenable listenable;
final Size size;
AnimationPaint(this.listenable, this.bodyColor, this.propColor, this.bodyValue, this.propColorValue, this.size) : super(repaint: listenable);
@override
void paint(Canvas canvas, Size size) {
Rect rect = Rect.fromLTWH(0, 0, size.width, size.height);
Paint paint = Paint()..color = Colors.red;
canvas.saveLayer(rect, paint);
drawBody(canvas, size);
drawEye(canvas, size);
drawLegumes(canvas, size);
canvas.restore();
// TODO: implement paint
}
drawBody(Canvas canvas,Size size){
final paint = Paint()..color = bodyColor!..style = PaintingStyle.fill..strokeWidth = 2;
var rect = Rect.fromCenter(
center: Offset(size.width / 2, size.height / 2), width: 100, height: 100);
// 结束角度;
var a = bodyValue.value * 40 / 180 * pi;
// 绘制圆弧
canvas.drawArc(rect, a, 2 * pi - a * 2, true, paint);
}
/*
* @author Marinda
* @date 2023/12/29 16:30
* @description 绘制眼睛 一黑一白
*/
drawEye(Canvas canvas,Size size){
//黑
Offset eyeOffset = Offset((size.width / 2),(size.height / 2) -30);
//白
Offset eye2Offset = Offset((size.width / 2) -3,(size.height / 2) -35);
Rect eyeRect = Rect.fromCenter(center: eyeOffset, width: 20, height: 20);
Rect eyeRect2 = Rect.fromCenter(center: eye2Offset, width: 5, height: 5);
final blackPaint = Paint()..color = Colors.black..strokeWidth = 2..style = PaintingStyle.fill;
final whitePaint = Paint()..color = Colors.white..strokeWidth = 2..style = PaintingStyle.fill;
canvas.drawOval(eyeRect,blackPaint);
canvas.drawOval(eyeRect2,whitePaint);
}
/*
* @author Marinda
* @date 2023/12/29 16:05
* @description 绘制豆
*/
drawLegumes(Canvas canvas,Size size){
Offset location = Offset((size.width / 2) + propColorValue.value,size.height /2);
Rect rect = Rect.fromCenter(center: location, width: 15, height: 15);
final paint = Paint()..color = propColor..strokeWidth = 2..style = PaintingStyle.fill;
// canvas.drawCircle(location, radius, paint);
canvas.drawOval(rect, paint);
}
@override
bool shouldRepaint(covariant AnimationPaint oldDelegate) {
return oldDelegate.listenable != listenable;
}
}
State
class CustomState extends State<CustomAnimation2> with TickerProviderStateMixin{
//圆弧角度
late Animation<double> animation;
//糖豆位置
late Animation<double> animation2;
Color? bodyColor;
Color? propColor;
late AnimationController controller;
late AnimationController controller2;
@override
void initState() {
controller = AnimationController(vsync: this,duration: Duration(milliseconds: 300),reverseDuration: Duration(milliseconds: 300));
controller2 = AnimationController(vsync: this,duration: Duration(milliseconds: 500));
animation = Tween<double>(begin: 0.2,end:1).animate(controller);
animation2 = Tween<double>(begin: 50,end: 0).animate(controller2);
controller.addStatusListener((status) {
// print('动画状态: ${status}');
if(status == AnimationStatus.completed){
controller.reverse();
}else if(status == AnimationStatus.dismissed){
controller.forward();
//已经执行完毕
controller2.reset();
controller2.forward();
propColor = randomColor();
setState(() {});
}
});
controller2.addStatusListener((status) {
if(status == AnimationStatus.completed){
bodyColor = propColor;
setState(() {});
}
});
controller2.forward();
controller.forward();
// TODO: implement initState
super.initState();
}
@override
void dispose() {
controller.dispose();
controller2.dispose();
super.dispose();
}
/*
* @author Marinda
* @date 2023/12/25 16:03
* @description 随机颜色
*/
Color randomColor(){
Color? color;
Random random = Random.secure();
int ranRed = random.nextInt(255).round();
int ranGreen = random.nextInt(255).round();
int ranBlue = random.nextInt(255).round();
color = Color.fromRGBO(ranRed,ranGreen,ranBlue,1);
return color;
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text("糖豆人动画",
style: TextStyle(
color: Colors.white,
fontSize: 18
)),
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios,size: 18,color: Colors.white,),
onPressed: () {
Navigator.pop(context);
},
),
),
body: Container(
child: Stack(
alignment: Alignment.center,
children: [
Container(
child: CustomPaint(
size: Size(size.width,size.height),
painter: AnimationPaint(
Listenable.merge([animation,animation2]),
bodyColor ?? Colors.black,
propColor ?? Colors.black,
animation,
animation2,
Size(300,300)
)
)
)
],
),
),
);
}
}
结束语
绘制其实很简单,无非就是传递控制动画结果以及相关弧形绘制,感谢你的观看!