| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- import 'dart:math';
- import 'package:flutter/material.dart';
- import 'package:keyboard/utils/common_expand.dart';
- import '../resource/assets.gen.dart';
- class HeartFillAnimation extends StatefulWidget {
- const HeartFillAnimation({
- super.key,
- required this.fillProgress,
- this.width = 250.0,
- this.onControllerCreated,
- });
- final double fillProgress;
- final double width;
- @override
- State createState() => _HeartFillAnimationState();
- final void Function(HeartFillController controller)? onControllerCreated;
- }
- class HeartFillController {
- late void Function() start;
- late void Function() stop;
- }
- class _HeartFillAnimationState extends State<HeartFillAnimation>
- with SingleTickerProviderStateMixin {
- late AnimationController _waveController;
- @override
- void initState() {
- super.initState();
- _waveController = AnimationController(
- vsync: this,
- duration: const Duration(milliseconds: 1000),
- )..repeat();
- if (widget.onControllerCreated != null) {
- final controller = HeartFillController();
- controller.start = () => _waveController.repeat();
- controller.stop = () => _waveController.stop();
- widget.onControllerCreated!(controller);
- }
- }
- @override
- void dispose() {
- _waveController.dispose();
- super.dispose();
- }
- void start() {
- _waveController.repeat();
- }
- void stop() {
- _waveController.stop();
- }
- @override
- Widget build(BuildContext context) {
- final height = widget.width * 208 / 250;
- return Stack(
- alignment: Alignment.center,
- children: [
- AnimatedBuilder(
- animation: _waveController,
- builder: (context, child) {
- return CustomPaint(
- size: Size(widget.width, height),
- painter: HeartFillPainter(
- fillProgress: widget.fillProgress,
- waveOffset: -_waveController.value * 2 * pi,
- ),
- );
- },
- ),
- Image(
- image: Assets.images.bgHeartFill.provider(),
- width: widget.width,
- height: height,
- fit: BoxFit.fill,
- ),
- ],
- );
- }
- }
- class HeartFillPainter extends CustomPainter {
- HeartFillPainter({required this.fillProgress, required this.waveOffset});
- final double fillProgress;
- final double waveOffset;
- static final _originalHeartPath = _createOriginalHeartPath();
- static Path _createOriginalHeartPath() {
- final path = Path();
- path.moveTo(182.981, 0);
- path.cubicTo(158.332, 0, 136.822, 13.4803, 125.435, 33.4817);
- path.cubicTo(125.07, 34.1387, 124.121, 34.1387, 123.756, 33.4817);
- path.cubicTo(112.344, 13.4803, 90.8336, 0, 66.1847, 0);
- path.cubicTo(29.6371, 0, 0, 29.6371, 0, 66.1847);
- path.cubicTo(0, 134.504, 85.9676, 188.719, 115.078, 205.119);
- path.cubicTo(121.041, 208.478, 128.123, 208.473, 134.081, 205.106);
- path.cubicTo(163.184, 188.66, 249.166, 134.307, 249.166, 66.1847);
- path.cubicTo(249.166, 29.6371, 219.529, 0, 182.981, 0);
- return path;
- }
- @override
- void paint(Canvas canvas, Size size) {
- // 应用缩放矩阵
- final scaleX = size.width / 250;
- final scaleY = size.height / 208;
- final matrix = Matrix4.identity()..scale(scaleX, scaleY);
- // 创建缩放后的爱心路径
- final heartPath = _originalHeartPath.transform(matrix.storage);
- // 设置裁剪区域
- canvas.save();
- canvas.clipPath(heartPath);
- // 计算填充高度
- final fillHeight = size.height * (1 - fillProgress);
- final fillRect = Rect.fromLTRB(0, fillHeight, size.width, size.height);
- // 绘制次级波浪
- final subWavePath = _createWavePath(
- fillHeight,
- size,
- offset: -pi / 5,
- amplitude: 15,
- frequency: 0.02,
- );
- canvas.drawPath(
- subWavePath,
- Paint()
- ..shader = LinearGradient(
- colors: ["#66FFB9D5".color, "#66FF219B".color],
- ).createShader(fillRect),
- );
- // 绘制主波浪
- final wavePath = _createWavePath(fillHeight, size);
- canvas.drawPath(
- wavePath,
- Paint()
- ..shader = LinearGradient(
- colors: ["#FF458C".color, "#FF74BC".color],
- ).createShader(fillRect),
- );
- canvas.restore();
- }
- Path _createWavePath(
- double fillHeight,
- Size size, {
- double offset = 0,
- double amplitude = 10.0,
- double frequency = 0.03,
- }) {
- final path = Path()..moveTo(0, fillHeight);
- for (double x = 0; x <= size.width; x++) {
- final y =
- fillHeight + sin(x * frequency + waveOffset + offset) * amplitude;
- path.lineTo(x, y);
- }
- path
- ..lineTo(size.width, size.height)
- ..lineTo(0, size.height)
- ..close();
- return path;
- }
- @override
- bool shouldRepaint(HeartFillPainter oldDelegate) {
- return fillProgress != oldDelegate.fillProgress ||
- waveOffset != oldDelegate.waveOffset;
- }
- }
|