Files
front-hokm/lib/feature/game/presentation/widgets/confetti.dart
T
2026-07-08 00:17:46 +03:30

120 lines
3.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:math' as math;
import 'package:flutter/material.dart';
/// بارشِ کاغذرنگی (کانفتی) سبک و بدونِ وابستگی — برای صفحه‌ی بردِ بازی.
class Confetti extends StatefulWidget {
final int count;
const Confetti({super.key, this.count = 60});
@override
State<Confetti> createState() => _ConfettiState();
}
class _ConfettiState extends State<Confetti> with SingleTickerProviderStateMixin {
late final AnimationController _c;
late final List<_Piece> _pieces;
static const _colors = [
Color(0xFFE9B949), // طلایی
Color(0xFF1E5FA8), // لاجوردی
Color(0xFFB81D2A), // شنگرف
Color(0xFF3FA34D), // سبز
Color(0xFFFFFFFF),
];
@override
void initState() {
super.initState();
final r = math.Random();
_pieces = List.generate(widget.count, (_) {
return _Piece(
x: r.nextDouble(),
delay: r.nextDouble() * 0.5,
speed: 0.7 + r.nextDouble() * 0.6,
drift: (r.nextDouble() - 0.5) * 0.4,
size: 6 + r.nextDouble() * 7,
color: _colors[r.nextInt(_colors.length)],
rot: r.nextDouble() * math.pi,
rotSpeed: (r.nextDouble() - 0.5) * 12,
round: r.nextBool(),
);
});
_c = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2600),
)..forward();
}
@override
void dispose() {
_c.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: AnimatedBuilder(
animation: _c,
builder: (_, __) => CustomPaint(
size: Size.infinite,
painter: _ConfettiPainter(_pieces, _c.value),
),
),
);
}
}
class _Piece {
final double x, delay, speed, drift, size, rot, rotSpeed;
final Color color;
final bool round;
_Piece({
required this.x,
required this.delay,
required this.speed,
required this.drift,
required this.size,
required this.color,
required this.rot,
required this.rotSpeed,
required this.round,
});
}
class _ConfettiPainter extends CustomPainter {
final List<_Piece> pieces;
final double t; // ۰..۱
_ConfettiPainter(this.pieces, this.t);
@override
void paint(Canvas canvas, Size size) {
for (final p in pieces) {
final local = ((t - p.delay) * p.speed).clamp(0.0, 1.0);
if (local <= 0) continue;
final y = -0.1 + local * 1.25;
final x = (p.x + p.drift * local) * size.width;
final py = y * size.height;
final opacity = (1.0 - local).clamp(0.0, 1.0);
final paint = Paint()..color = p.color.withValues(alpha: opacity);
canvas.save();
canvas.translate(x, py);
canvas.rotate(p.rot + p.rotSpeed * local);
if (p.round) {
canvas.drawCircle(Offset.zero, p.size / 2, paint);
} else {
canvas.drawRect(
Rect.fromCenter(center: Offset.zero, width: p.size, height: p.size * 0.6),
paint,
);
}
canvas.restore();
}
}
@override
bool shouldRepaint(covariant _ConfettiPainter old) => old.t != t;
}