绘制加载动画:由小圆组成的大圆
- 1. 定义 LoadingScreen 类
- 2. 实现 _LoadingScreenState 类
- 3. 定义 LoadingPainter 类
- 4. 总结
实现加载动画
我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。
效果展示视频地址:https://live.csdn.net/v/417383
资源文件下载地址:https://download.csdn.net/download/yang_6799/89639107
1. 定义 LoadingScreen 类
LoadingScreen 类是一个 StatefulWidget,它管理 AnimationController 和 Animation 对象。AnimationController 用于控制动画的播放,Animation 对象则表示动画的具体值。
import 'package:flutter/material.dart';
import 'dart:math';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: LoadingScreen(),
);
}
}
以上代码定义了 MyApp 和 LoadingScreen 两个类,其中 MyApp 是应用的入口点,而 LoadingScreen 则是主要的动画屏幕。
2. 实现 _LoadingScreenState 类
在 LoadingScreen 类中,我们要实现 _LoadingScreenState,它是实际负责动画逻辑的地方。
class LoadingScreen extends StatefulWidget {
@override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen>
with SingleTickerProviderStateMixin {
AnimationController? _controller;
Animation<double>? _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
)..repeat();
_animation = Tween<double>(begin: 0, end: 1).animate(_controller!);
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Center(
child: CustomPaint(
painter: LoadingPainter(animation: _animation!),
child: SizedBox(
width: 200.0,
height: 200.0,
),
),
),
);
}
}
详解
- initState 方法:
- 初始化时创建 AnimationController 并设置动画持续时间为 3 秒。
- 调用 …repeat() 方法让动画重复播放。
- 使用 Tween 创建一个从 0 到 1 的动画,并与控制器关联。
- dispose 方法:
- 销毁控制器以释放资源。
- build 方法:
- 使用 CustomPaint 来绘制自定义内容,这里我们指定了 LoadingPainter 作为画笔,然后设置了一个 200x200 的 SizedBox 来容纳绘制内容。
3. 定义 LoadingPainter 类
LoadingPainter 类继承自 CustomPainter,负责实际绘制每个小圆。我们需要计算每个小圆的位置、大小和透明度,以便实现顺时针方向依次从小到大的动画效果。
class LoadingPainter extends CustomPainter {
final Animation<double> animation;
LoadingPainter({required this.animation}) : super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
double radius = size.width / 2;
int circleCount = 12;
double baseCircleRadius = 5.0;
double maxCircleRadius = 15.0;
Paint paint = Paint()..style = PaintingStyle.fill;
for (int i = 0; i < circleCount; i++) {
double angle = (i / circleCount) * 2 * pi;
double x = radius + radius * cos(angle);
double y = radius + radius * sin(angle);
// 计算每个小圆在动画中的进度,并加入相位偏移
double progress = (animation.value - i / circleCount) % 1;
double scaleFactor = (sin(progress * 2 * pi) + 1) / 2;
double currentRadius =
baseCircleRadius + scaleFactor * (maxCircleRadius - baseCircleRadius);
// 确保透明度不低于 0.3
double opacity = 0.3 + 0.7 * scaleFactor;
paint.color = Colors.blue.withOpacity(opacity);
canvas.drawCircle(Offset(x, y), currentRadius, paint);
}
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
详解
- 构造函数:
- 接受一个 Animation 类型参数,并调用 super(repaint: animation) 以便在动画值改变时触发重绘。
- paint 方法:
- 初始化一些常量,包括大圆的半径、小圆的数量以及小圆的最小和最大半径。
- 使用 Paint 进行绘制配置。
- 遍历每个小圆,并根据其序号和当前动画进度计算位置和大小。
- 通过 sin 函数计算缩放因子 scaleFactor,并使用该因子调整小圆的半径和透明度。
- 最后,通过 canvas.drawCircle 在计算出的坐标处绘制每个小圆。
4. 总结
在这篇文章中,我们学习了如何使用 Flutter 创建一个加载动画。通过 AnimationController 和 CustomPainter,我们可以轻松地实现各种复杂的动画效果。这种加载动画不仅可以提升用户体验,还可以让您的应用看起来更加专业。