实现唯美的小心心雪花动画效果
在这篇博客中,我们将带你一步步实现一个浪漫的小心心雪花动画效果。这个动画非常适合用于情人节主题、表白页面或者任何需要增加浪漫气息的应用场景。
我们将构建一个主要的 HeartFallAnimation widget 来管理和绘制我们的动画。
效果展示视频地址:https://live.csdn.net/v/417675
资源文件下载地址:https://download.csdn.net/download/yang_6799/89641781
1. 项目结构
整个项目基于一个单一的 StatefulWidget HeartFallAnimation,它将负责初始化、更新和绘制心形雪花。
2. 创建核心类和方法
首先,我们定义一个表示心形的 Heart 类。这个类包含 x 和 y 坐标,以及心形的下落速度。这样可以使每个心形以不同的速度从屏幕顶部掉落,模拟真实的雪花效果。
class Heart {
double x, y, speed;
Heart(this.x, this.y, this.speed);
void updatePosition(double screenHeight) {
y += speed;
if (y > screenHeight) {
y = -100; // 重置心形位置到屏幕顶部之外,使其重新开始下落
x = Random().nextDouble() * screenHeight; // 随机重置x坐标
}
}
}
- x 和 y:心形在屏幕上的位置。
- speed:心形的下落速度,每个心形的速度是随机的。
- updatePosition 方法:更新心形的位置。如果心形超出了屏幕底部,它会被重置到屏幕顶部之外并赋予新的 x 坐标。
接下来,我们实现 HeartFallPainter,这个类负责绘制所有下落的心形:
class HeartFallPainter extends CustomPainter {
final List<Heart> hearts;
final Paint heartPaint = Paint()..color = Colors.red;
HeartFallPainter(this.hearts);
@override
void paint(Canvas canvas, Size size) {
for (var heart in hearts) {
drawHeart(canvas, heart.x, heart.y, size.width / 40); // 调整心形的大小
}
}
void drawHeart(Canvas canvas, double x, double y, double size) {
final path = Path();
path.moveTo(x, y);
path.cubicTo(
x - size / 2, y - size / 2,
x - size, y + size / 3,
x, y + size,
);
path.moveTo(x, y);
path.cubicTo(
x + size / 2, y - size / 2,
x + size, y + size / 3,
x, y + size,
);
canvas.drawPath(path, heartPaint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
- hearts:传递给画家的一组心形对象。
- heartPaint:设置心形的颜色为红色。
- paint 方法:遍历所有心形并调用 drawHeart 方法进行绘制。
- drawHeart 方法:在指定位置绘制心形路径。使用贝塞尔曲线来绘制心形的左右两半。
然后,我们创建 HeartFallAnimation 这个 StatefulWidget 它将控制心形动画的逻辑:
class HeartFallAnimation extends StatefulWidget {
@override
_HeartFallAnimationState createState() => _HeartFallAnimationState();
}
class _HeartFallAnimationState extends State<HeartFallAnimation>
with SingleTickerProviderStateMixin {
AnimationController? _controller;
List<Heart> hearts = [];
static const int numberOfHearts = 200; // 增加小心心的数量
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
)..repeat();
_controller!.addListener(() {
setState(() {
for (var heart in hearts) {
heart.updatePosition(MediaQuery.of(context).size.height);
}
});
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 初始化心形列表
if (hearts.isEmpty) {
final screenWidth = MediaQuery.of(context).size.width;
for (int i = 0; i < numberOfHearts; i++) {
double startX = Random().nextDouble() * screenWidth;
double startY = -Random().nextDouble() * 1000;
double fallSpeed = Random().nextDouble() * 2 + 1;
hearts.add(Heart(startX, startY, fallSpeed));
}
}
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomPaint(
painter: HeartFallPainter(hearts),
child: Container(),
),
);
}
}
_HeartFallAnimationState 类包含以下重要部分:
- AnimationController:控制动画的刷新频率。
- _controller = AnimationController(vsync: this, duration: Duration(seconds: 5))…repeat(); 设置动画持续时间 5 秒钟并重复播放。
- initState:初始化心形数据和动画控制器。
- _controller!.addListener(() { … }):通过监听动画控制器的变化来不断更新心形位置。
- didChangeDependencies:在依赖改变时(例如屏幕尺寸),初始化心形位置。
- final screenWidth = MediaQuery.of(context).size.width; 获取屏幕宽度。
- 使用 Random 类生成随机的初始位置和速度,为每个心形添加到 hearts 列表中。
- dispose:释放动画控制器资源。
- build:构建 UI,使用 CustomPaint 绘制心形。
3. 调整心形数量和大小
为了让效果更加显著,你可以根据需要调整心形的数量和大小。例如,在上面的代码中,我们将 numberOfHearts 设置为 200,并调整了心形大小。
static const int numberOfHearts = 200; // 调整心形数量
为了提高心形的密集度,我们将心形绘制尺寸调整为画布宽度的 1/40:
drawHeart(canvas, heart.x, heart.y, size.width / 40); // 调整心形大小
4. 总结
通过上述步骤,我们成功地创建了一个满屏小心心从顶部掉落的动画效果。这个动画不仅美观,还能为你的应用增添一份特别的浪漫氛围。
希望这篇博客能帮助你更好地理解如何使用动画和自定义绘图。如果你有任何问题或建议,欢迎在评论区留言!