2026-04-09 21:32:23 +08:00

327 lines
8.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:math';
import 'dart:async';
import 'package:yumi/ui_kit/widgets/headdress/headdress_widget.dart';
import 'package:yumi/ui_kit/widgets/room/anim/room_entrance_widget.dart';
// 红包数据模型
class RedPacket {
double x; // x坐标
double y; // y坐标
double speed; // 下落速度
double size; // 大小
double rotation; // 旋转角度
double rotationSpeed; // 旋转速度
Color color; // 红包颜色
bool isCollected; // 是否被收集
String id; // 唯一标识符
RedPacket({
required this.x,
required this.y,
required this.speed,
required this.size,
required this.rotation,
required this.rotationSpeed,
required this.color,
this.isCollected = false,
required this.id,
});
}
class RedPacketRainPage extends StatefulWidget {
@override
_RedPacketRainPageState createState() => _RedPacketRainPageState();
}
class _RedPacketRainPageState extends State<RedPacketRainPage>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
final Random _random = Random();
final List<RedPacket> _redPackets = [];
final int _maxPackets = 50;
DateTime? _startTime;
int _durationSeconds = 30;
int _collectedCount = 0;
final List<Color> _redColors = [
Colors.red[400]!,
Colors.red[500]!,
Colors.red[600]!,
Colors.red[700]!,
Colors.red[800]!,
Colors.red[900]!,
];
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 350),
)..addListener(_updateRain);
_startRain();
Future.delayed(Duration(seconds: 3),(){
RoomEntranceQueueManager().addToQueue(AnimationQueueItem(resource: "sc_images/room/entrance/test3.svga",dynamicData: {"textReplacements":{"02":"3123123"}}));
});
}
void _startRain() {
_startTime = DateTime.now();
_collectedCount = 0;
_redPackets.clear();
_controller.repeat();
Timer(Duration(seconds: _durationSeconds), () {
if (mounted) {
_controller.stop();
}
});
}
void _updateRain() {
if (!mounted) return;
final screenWidth = MediaQuery.of(context).size.width;
final screenHeight = MediaQuery.of(context).size.height;
// 随机生成新红包
if (_redPackets.length < _maxPackets && _random.nextDouble() < 0.3) {
_createNewPacket(screenWidth, screenHeight);
}
// 更新现有红包位置
for (int i = _redPackets.length - 1; i >= 0; i--) {
final packet = _redPackets[i];
if (!packet.isCollected) {
packet.y += packet.speed;
packet.rotation += packet.rotationSpeed;
if (packet.y > screenHeight + 100) {
_redPackets.removeAt(i);
continue;
}
}
}
setState(() {});
}
void _createNewPacket(double screenWidth, double screenHeight) {
_redPackets.add(
RedPacket(
x: _random.nextDouble() * screenWidth,
y: -50,
speed: 2 + _random.nextDouble() * 3,
size: 30 + _random.nextDouble() * 20,
rotation: _random.nextDouble() * 2 * pi,
rotationSpeed: (_random.nextDouble() - 0.5) * 0.1,
color: _redColors[_random.nextInt(_redColors.length)],
id: '${DateTime.now().millisecondsSinceEpoch}_${_random.nextInt(1000)}',
),
);
}
void _collectPacket(String packetId) {
final packetIndex = _redPackets.indexWhere(
(packet) => packet.id == packetId,
);
if (packetIndex != -1 && !_redPackets[packetIndex].isCollected) {
setState(() {
_redPackets[packetIndex].isCollected = true;
_collectedCount++;
});
// 收集动画效果
Future.delayed(Duration(milliseconds: 350), () {
if (mounted) {
setState(() {
_redPackets.removeWhere((packet) => packet.id == packetId);
});
}
});
}
}
@override
void dispose() {
_controller.dispose();
_redPackets.clear();
super.dispose();
}
@override
Widget build(BuildContext context) {
final remainingTime =
_startTime != null
? max(
0,
_durationSeconds -
DateTime.now().difference(_startTime!).inSeconds,
)
: _durationSeconds;
return Scaffold(
backgroundColor: Colors.black,
body: RoomEntranceWidget(
height: 90.w,
),
);
}
void _handleTap(Offset position) {
// 从后往前遍历,这样点击检测会优先处理最上层的红包(最后绘制的)
for (int i = _redPackets.length - 1; i >= 0; i--) {
final packet = _redPackets[i];
if (!packet.isCollected && _isPointInRedPacket(position, packet)) {
_collectPacket(packet.id);
break; // 只处理最上层的一个红包
}
}
}
bool _isPointInRedPacket(Offset point, RedPacket packet) {
// 简化的点击区域检测 - 圆形区域
final distance = sqrt(
pow(point.dx - packet.x, 2) + pow(point.dy - packet.y, 2),
);
return distance < packet.size;
}
Widget _buildInfoCard(String title, String value) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.red),
),
child: Column(
children: [
Text(title, style: TextStyle(color: Colors.white, fontSize: 10)),
Text(
value,
style: TextStyle(
color: Colors.red,
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
],
),
);
}
}
class RedPacketPainter extends CustomPainter {
final List<RedPacket> redPackets;
RedPacketPainter({required this.redPackets});
@override
void paint(Canvas canvas, Size size) {
final textPainter = TextPainter(textDirection: TextDirection.ltr);
for (final packet in redPackets) {
if (packet.isCollected) {
_drawCollectedPacket(canvas, packet);
} else {
_drawRedPacket(canvas, packet, textPainter);
}
}
}
void _drawRedPacket(
Canvas canvas,
RedPacket packet,
TextPainter textPainter,
) {
canvas.save();
canvas.translate(packet.x, packet.y);
canvas.rotate(packet.rotation);
// 绘制红包主体
final packetPaint =
Paint()
..color = packet.color
..style = PaintingStyle.fill;
final rect = Rect.fromCenter(
center: Offset.zero,
width: packet.size,
height: packet.size * 1.5,
);
final rrect = RRect.fromRectAndRadius(rect, Radius.circular(8));
canvas.drawRRect(rrect, packetPaint);
// 红包中间的黄色条纹
final stripePaint =
Paint()
..color = Colors.yellow[700]!
..style = PaintingStyle.fill;
final stripeRect = Rect.fromCenter(
center: Offset.zero,
width: packet.size * 0.8,
height: packet.size * 0.2,
);
canvas.drawRRect(
RRect.fromRectAndRadius(stripeRect, Radius.circular(4)),
stripePaint,
);
// 红包文字
textPainter.text = TextSpan(
text: '',
style: TextStyle(
color: Colors.yellow[700],
fontSize: packet.size * 0.4,
fontWeight: FontWeight.bold,
),
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(-textPainter.width / 2, -textPainter.height / 2),
);
canvas.restore();
}
void _drawCollectedPacket(Canvas canvas, RedPacket packet) {
final progress = (DateTime.now().millisecondsSinceEpoch % 300) / 300;
final scale = 1.0 - progress;
final opacity = 1.0 - progress;
canvas.save();
canvas.translate(packet.x, packet.y);
canvas.scale(scale);
final packetPaint =
Paint()
..color = packet.color.withOpacity(opacity)
..style = PaintingStyle.fill;
final rect = Rect.fromCenter(
center: Offset.zero,
width: packet.size,
height: packet.size * 1.5,
);
final rrect = RRect.fromRectAndRadius(rect, Radius.circular(8));
canvas.drawRRect(rrect, packetPaint);
canvas.restore();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}