113 lines
2.9 KiB
Dart
113 lines
2.9 KiB
Dart
import 'dart:math';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class VoiceWaveAnimation extends StatefulWidget {
|
|
const VoiceWaveAnimation({super.key});
|
|
|
|
@override
|
|
State<VoiceWaveAnimation> createState() => _VoiceWaveAnimationState();
|
|
}
|
|
|
|
class _VoiceWaveAnimationState extends State<VoiceWaveAnimation> with TickerProviderStateMixin {
|
|
late AnimationController _controller;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
// 创建一个持续动画控制器
|
|
_controller = AnimationController(
|
|
duration: const Duration(milliseconds: 1500),
|
|
vsync: this,
|
|
)..repeat();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: _controller,
|
|
builder: (context, child) {
|
|
return SizedBox(
|
|
width: 160,
|
|
height: 30,
|
|
child: CustomPaint(
|
|
painter: SineWavePainter(
|
|
animation: _controller,
|
|
count: 2, // 波浪数量
|
|
color: Colors.white,
|
|
amplitudeFactor: 0.5, // 波幅因子
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class SineWavePainter extends CustomPainter {
|
|
final Animation<double> animation;
|
|
final int count; // 波浪数量
|
|
final Color color;
|
|
final double amplitudeFactor; // 波幅因子
|
|
|
|
SineWavePainter({
|
|
required this.animation,
|
|
this.count = 2,
|
|
required this.color,
|
|
this.amplitudeFactor = 1.0,
|
|
});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final Paint paint = Paint()
|
|
..color = color.withValues(alpha: 0.7)
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 2.0;
|
|
|
|
final Paint paint2 = Paint()
|
|
..color = color.withValues(alpha: 0.3)
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 2.0;
|
|
|
|
final double width = size.width;
|
|
final double height = size.height;
|
|
final double centerY = height / 2;
|
|
|
|
// 绘制第一个波浪
|
|
final path = Path();
|
|
final path2 = Path();
|
|
|
|
// 创建两条不同相位的波形,一条主要一条次要
|
|
path.moveTo(0, centerY);
|
|
path2.moveTo(0, centerY);
|
|
|
|
for (int i = 0; i < width; i++) {
|
|
// 主波形
|
|
final double x = i.toDouble();
|
|
final double normalizedX = (x / width) * count * 2 * pi;
|
|
final double offset = 2 * pi * animation.value;
|
|
|
|
// 使用随机振幅变化模拟声音波动
|
|
final double randomAmplitude = (sin(normalizedX + offset) + 1) / 2 * amplitudeFactor;
|
|
path.lineTo(x, centerY - randomAmplitude * 10);
|
|
|
|
// 次波形,相位滞后
|
|
final double randomAmplitude2 = (sin(normalizedX + offset + pi / 6) + 1) / 2 * amplitudeFactor;
|
|
path2.lineTo(x, centerY - randomAmplitude2 * 5);
|
|
}
|
|
|
|
canvas.drawPath(path, paint);
|
|
canvas.drawPath(path2, paint2);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
|
return true;
|
|
}
|
|
} |