| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- import 'dart:math';
- import 'package:flame/components.dart';
- import 'package:flame/effects.dart';
- import 'package:flame/events.dart';
- import 'package:flutter/material.dart';
- import '../../utils/colors.dart';
- class Bubble extends CircleComponent with HasGameReference, TapCallbacks {
- static const double defaultRadius = 30.0;
- static const double maxRadius = 50.0;
- static const double minRadius = 20.0;
-
- late Color bubbleColor;
- bool isPopping = false;
- Function(Bubble)? onPop;
-
- Bubble({
- required Vector2 position,
- double? radius,
- this.onPop,
- }) : super(
- radius: radius ?? _randomRadius(),
- position: position,
- anchor: Anchor.center,
- );
- static double _randomRadius() {
- final random = Random();
- return minRadius + random.nextDouble() * (maxRadius - minRadius);
- }
- @override
- Future<void> onLoad() async {
- await super.onLoad();
-
- // Set random bubble color with transparency
- bubbleColor = _getRandomBubbleColor();
- paint = Paint()
- ..color = bubbleColor.withValues(alpha: 0.8)
- ..style = PaintingStyle.fill;
-
- // Add a subtle floating animation
- add(
- MoveEffect.by(
- Vector2(0, -10),
- EffectController(
- duration: 2.0,
- alternate: true,
- infinite: true,
- ),
- ),
- );
-
- // Add a gentle scaling animation
- add(
- ScaleEffect.by(
- Vector2.all(0.1),
- EffectController(
- duration: 1.5,
- alternate: true,
- infinite: true,
- ),
- ),
- );
- }
- Color _getRandomBubbleColor() {
- final random = Random();
- final colors = [
- ZenColors.bubbleDefault,
- ZenColors.defaultLink,
- ZenColors.hoverLink,
- ZenColors.lightModeHover,
- ZenColors.buttonBackground,
- ];
- return colors[random.nextInt(colors.length)];
- }
- @override
- bool onTapDown(TapDownEvent event) {
- if (!isPopping) {
- pop();
- }
- return true;
- }
- void pop() {
- if (isPopping) return;
-
- isPopping = true;
-
- // Create pop animation
- final popEffect = ScaleEffect.to(
- Vector2.all(1.5),
- EffectController(duration: 0.2),
- );
-
- final fadeEffect = OpacityEffect.to(
- 0.0,
- EffectController(duration: 0.2),
- );
-
- add(popEffect);
- add(fadeEffect);
-
- // Create particle effects
- _createPopParticles();
-
- // Notify parent and remove bubble
- onPop?.call(this);
-
- Future.delayed(const Duration(milliseconds: 200), () {
- if (isMounted) {
- removeFromParent();
- }
- });
- }
- void _createPopParticles() {
- final random = Random();
- const particleCount = 8;
-
- for (int i = 0; i < particleCount; i++) {
- final angle = (i / particleCount) * 2 * pi;
- final particleVelocity = Vector2(
- cos(angle) * (50 + random.nextDouble() * 30),
- sin(angle) * (50 + random.nextDouble() * 30),
- );
-
- final particle = CircleComponent(
- radius: 3 + random.nextDouble() * 3,
- position: position.clone(),
- paint: Paint()
- ..color = bubbleColor.withValues(alpha: 0.8)
- ..style = PaintingStyle.fill,
- );
-
- parent?.add(particle);
-
- // Add movement effect to particle
- particle.add(
- MoveEffect.by(
- particleVelocity,
- EffectController(duration: 0.5),
- ),
- );
-
- // Add fade effect to particle
- particle.add(
- OpacityEffect.to(
- 0.0,
- EffectController(duration: 0.5),
- ),
- );
-
- // Remove particle after animation
- Future.delayed(const Duration(milliseconds: 500), () {
- if (particle.isMounted) {
- particle.removeFromParent();
- }
- });
- }
- }
- }
|