diff --git a/flare_flutter/lib/flare_controls.dart b/flare_flutter/lib/flare_controls.dart index ed89a16..6f286ad 100644 --- a/flare_flutter/lib/flare_controls.dart +++ b/flare_flutter/lib/flare_controls.dart @@ -4,6 +4,14 @@ import 'flare.dart'; import 'flare_actor.dart'; import 'flare_controller.dart'; +/// [TimedAnimation] holds a reference to [FlareAnimationLayer]s +/// and [currentTime] maintains that layers current animation timeline +/// for mixing purposes +class TimedAnimation { + FlareAnimationLayer layer; + double currentTime; // how many seconds have elapsed +} + /// [FlareControls] is a concrete implementation of the [FlareController]. /// /// This controller will provide some basic functionality, such as @@ -15,10 +23,13 @@ class FlareControls extends FlareController { /// The current [ActorAnimation]. String _animationName; - final double _mixSeconds = 0.1; - /// The [FlareAnimationLayer]s currently active. - final List _animationLayers = []; + /// The [TimedAnimation]s currently active. + final List _animations = []; + + /// Used as a reference for each animation layer + /// to stay in sync + double _ticker = 0.0; /// Called at initialization time, it stores the reference /// to the current [FlutterActorArtboard]. @@ -34,15 +45,23 @@ class FlareControls extends FlareController { /// to the end of the list of currently playing animation layers. void play(String name, {double mix = 1.0, double mixSeconds = 0.2}) { _animationName = name; + if (_animationName != null && _artboard != null) { + int layerIndex = _animations.indexWhere((ani) => ani.layer.name == name); ActorAnimation animation = _artboard.getAnimation(_animationName); - if (animation != null) { - _animationLayers.add(FlareAnimationLayer() + + if (animation != null && layerIndex == -1) { + _animations.add(TimedAnimation() + ..layer = (FlareAnimationLayer() ..name = _animationName ..animation = animation ..mix = mix - ..mixSeconds = mixSeconds); + ..mixSeconds = mixSeconds) + ..currentTime = mix * mixSeconds); isActive.value = true; + } else if (layerIndex >= 0) { + /// If we already have reference to this, update the seconds + _animations[layerIndex].layer.mixSeconds = mixSeconds; } } } @@ -58,19 +77,20 @@ class FlareControls extends FlareController { @override bool advance(FlutterActorArtboard artboard, double elapsed) { /// List of completed animations during this frame. - List completed = []; + List completed = []; + + _ticker += elapsed; /// This loop will mix all the currently active animation layers so that, /// if an animation is played on top of the current one, it'll smoothly mix - /// between the two instead of immediately switching to the new one. - for (int i = 0; i < _animationLayers.length; i++) { - FlareAnimationLayer layer = _animationLayers[i]; - layer.mix += elapsed; - layer.time += elapsed; + /// between the two instead of immediately switching to the new one. + for (int i = 0; i < _animations.length; i++) { + FlareAnimationLayer layer = _animations[i].layer; + layer.time = _ticker; + _animations[i].currentTime += layer.name == _animationName ? elapsed : -elapsed; + _animations[i].currentTime = max(0.0, min(layer.mixSeconds, _animations[i].currentTime)); - double mix = (_mixSeconds == null || _mixSeconds == 0.0) - ? 1.0 - : min(1.0, layer.mix / _mixSeconds); + layer.mix = max(0.0, min(1.0, _animations[i].currentTime / layer.mixSeconds)); /// Loop the time if needed. if (layer.animation.isLooping) { @@ -78,19 +98,19 @@ class FlareControls extends FlareController { } /// Apply the animation with the current mix. - layer.animation.apply(layer.time, _artboard, mix); + layer.animation.apply(layer.time, _artboard, layer.mix); - /// Add (non-looping) finished animations to the list. - if (layer.time > layer.animation.duration) { - completed.add(layer); + /// Axe it after it's finished mixing + if (layer.mix == 0) { + completed.add(_animations[i]); } } /// Notify of the completed animations. - for (final FlareAnimationLayer animation in completed) { - _animationLayers.remove(animation); - onCompleted(animation.name); + for (final TimedAnimation animation in completed) { + _animations.remove(animation); + onCompleted(animation.layer.name); } - return _animationLayers.isNotEmpty; + return _animations.isNotEmpty; } }