Explorar el Código

Merge remote-tracking branch 'origin/v1.0.0' into v1.0.0

hezihao hace 7 meses
padre
commit
c0b0ecc493
Se han modificado 2 ficheros con 166 adiciones y 0 borrados
  1. BIN
      assets/images/bg_heart_fill.png
  2. 166 0
      lib/widget/heart_fill_view.dart

BIN
assets/images/bg_heart_fill.png


+ 166 - 0
lib/widget/heart_fill_view.dart

@@ -0,0 +1,166 @@
+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,
+  });
+
+  final double fillProgress;
+  final double width;
+
+  @override
+  State createState() => _HeartFillAnimationState();
+}
+
+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();
+  }
+
+  @override
+  void dispose() {
+    _waveController.dispose();
+    super.dispose();
+  }
+
+  @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;
+  }
+}