diff --git a/lib/pangea/analytics/widgets/gain_points.dart b/lib/pangea/analytics/widgets/gain_points.dart index 0e2b09eac..bc9ea08d5 100644 --- a/lib/pangea/analytics/widgets/gain_points.dart +++ b/lib/pangea/analytics/widgets/gain_points.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'package:flutter/material.dart'; @@ -29,6 +30,9 @@ class PointsGainedAnimationState extends State late AnimationController _controller; late Animation _offsetAnimation; late Animation _fadeAnimation; + final List> _swayAnimation = []; + final List _randomSwayOffset = []; + final List _particleTrajectories = []; StreamSubscription? _pointsSubscription; int? get _prevXP => @@ -37,17 +41,19 @@ class PointsGainedAnimationState extends State MatrixState.pangeaController.getAnalytics.constructListModel.totalXP; int? _addedPoints; + final Random _random = Random(); + @override void initState() { super.initState(); _controller = AnimationController( - duration: const Duration(seconds: 2), + duration: const Duration(milliseconds: 2000), vsync: this, ); _offsetAnimation = Tween( - begin: const Offset(0.0, 0.0), - end: const Offset(0.0, -1.0), + begin: const Offset(0.0, 0), + end: const Offset(0.0, -3), ).animate( CurvedAnimation( parent: _controller, @@ -70,6 +76,42 @@ class PointsGainedAnimationState extends State .listen(_showPointsGained); } + void initParticleTrajectories() { + _particleTrajectories.clear(); + for (int i = 0; i < (_addedPoints?.abs() ?? 0); i++) { + final angle = _random.nextDouble() * (pi / 2) + + pi / 4; // Random angle in the V-shaped range. + const baseSpeed = 20; // Initial base speed. + const exponentialFactor = 30; // Factor for exponential growth. + final speedMultiplier = _random.nextDouble(); // Random speed multiplier. + final speed = baseSpeed * + pow(exponentialFactor, speedMultiplier); // Exponential speed. + _particleTrajectories + .add(Offset(speed * cos(angle), -speed * sin(angle))); + } + } + + void initSwayAnimations() { + _swayAnimation.clear(); + _randomSwayOffset.clear(); + initParticleTrajectories(); + + for (int i = 0; i < (_addedPoints ?? 0); i++) { + _swayAnimation.add( + Tween( + begin: 0.0, + end: 2 * pi, + ).animate( + CurvedAnimation( + parent: _controller, + curve: Curves.linear, + ), + ), + ); + _randomSwayOffset.add(_random.nextDouble() * 2 * pi); + } + } + @override void dispose() { _controller.dispose(); @@ -81,6 +123,7 @@ class PointsGainedAnimationState extends State if (update.origin != widget.origin) return; setState(() => _addedPoints = (_currentXP ?? 0) - (_prevXP ?? 0)); if (_prevXP != _currentXP) { + initSwayAnimations(); _controller.reset(); _controller.forward(); } @@ -98,19 +141,41 @@ class PointsGainedAnimationState extends State final textColor = _addedPoints! > 0 ? widget.gainColor : widget.loseColor; + final plusWidget = Text( + _addedPoints! > 0 ? "+" : "-", + style: BotStyle.text( + context, + big: true, + setColor: textColor == null, + existingStyle: TextStyle( + color: textColor, + ), + ), + ); + return SlideTransition( position: _offsetAnimation, child: FadeTransition( opacity: _fadeAnimation, - child: Text( - '${_addedPoints! > 0 ? '+' : ''}$_addedPoints', - style: BotStyle.text( - context, - big: true, - setColor: textColor == null, - existingStyle: TextStyle( - color: textColor, - ), + child: IgnorePointer( + ignoring: _controller.isAnimating, + child: Stack( + children: List.generate(_addedPoints!.abs(), (index) { + return AnimatedBuilder( + animation: _controller, + builder: (context, child) { + final progress = _controller.value; + final trajectory = _particleTrajectories[index]; + return Transform.translate( + offset: Offset( + trajectory.dx * pow(progress, 2), + trajectory.dy * pow(progress, 2), + ), + child: plusWidget, + ); + }, + ); + }), ), ), ),