You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
2.5 KiB
104 lines
2.5 KiB
import 'dart:ui';
|
|
import 'package:flutter/material.dart';
|
|
|
|
/// 使用Shader实现的麦克风流动动画组件
|
|
class ShaderMicFlow extends StatefulWidget {
|
|
final double size;
|
|
final double amplitude;
|
|
|
|
const ShaderMicFlow({
|
|
super.key,
|
|
required this.size,
|
|
this.amplitude = 0.0,
|
|
});
|
|
|
|
@override
|
|
State<ShaderMicFlow> createState() => _ShaderMicFlowState();
|
|
}
|
|
|
|
class _ShaderMicFlowState extends State<ShaderMicFlow>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _controller;
|
|
FragmentShader? _shader;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = AnimationController(
|
|
duration: const Duration(seconds: 60),
|
|
vsync: this,
|
|
)..repeat();
|
|
|
|
_loadShader();
|
|
}
|
|
|
|
Future<void> _loadShader() async {
|
|
try {
|
|
final program = await FragmentProgram.fromAsset('shaders/mic_flow.frag');
|
|
setState(() {
|
|
_shader = program.fragmentShader();
|
|
});
|
|
} catch (e) {
|
|
debugPrint('Failed to load shader: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
_shader?.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SizedBox(
|
|
width: widget.size,
|
|
height: widget.size,
|
|
child: AnimatedBuilder(
|
|
animation: _controller,
|
|
builder: (context, child) {
|
|
return CustomPaint(
|
|
painter: _shader != null
|
|
? _ShaderPainter(
|
|
shader: _shader!,
|
|
time: _controller.value * 60.0,
|
|
amplitude: widget.amplitude,
|
|
)
|
|
: null,
|
|
size: Size.square(widget.size),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ShaderPainter extends CustomPainter {
|
|
final FragmentShader shader;
|
|
final double time;
|
|
final double amplitude;
|
|
|
|
_ShaderPainter({
|
|
required this.shader,
|
|
required this.time,
|
|
required this.amplitude,
|
|
});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
// 设置shader uniforms
|
|
shader.setFloat(0, size.width); // u_resolution.x
|
|
shader.setFloat(1, size.height); // u_resolution.y
|
|
shader.setFloat(2, time); // u_time
|
|
shader.setFloat(3, amplitude); // u_amp
|
|
|
|
final paint = Paint()..shader = shader;
|
|
canvas.drawRect(Offset.zero & size, paint);
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(covariant _ShaderPainter oldDelegate) {
|
|
return oldDelegate.time != time || oldDelegate.amplitude != amplitude;
|
|
}
|
|
}
|