diff --git a/src/Avalonia.Animation/AnimationInstance`1.cs b/src/Avalonia.Animation/AnimationInstance`1.cs index 5a72904ed2..c264663b56 100644 --- a/src/Avalonia.Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Animation/AnimationInstance`1.cs @@ -154,7 +154,7 @@ namespace Avalonia.Animation private void InternalStep(TimeSpan systemTime) { DoPlayStatesAndTime(systemTime); - + var time = _internalClock - _firstFrameCount; var delayEndpoint = _delay; var iterationEndpoint = delayEndpoint + _duration; @@ -188,10 +188,7 @@ namespace Avalonia.Animation if (!_isLooping) { - if (_currentIteration > _repeatCount) - DoComplete(); - - if (time > iterationEndpoint) + if ((_currentIteration > _repeatCount) | (time > iterationEndpoint)) DoComplete(); } diff --git a/src/Avalonia.Animation/Animator`1.cs b/src/Avalonia.Animation/Animator`1.cs index decca8e858..0de3991a88 100644 --- a/src/Avalonia.Animation/Animator`1.cs +++ b/src/Avalonia.Animation/Animator`1.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; @@ -38,10 +41,8 @@ namespace Avalonia.Animation if (!_isVerifiedAndConverted) VerifyConvertKeyFrames(); - return match.DistinctUntilChanged() - .Select(x => x ? RunKeyFrames(animation, control, onComplete) : null) - .DisposeCurrentOnNext() - .Subscribe(); + var subject = new DisposeAnimationInstanceObservable(this, animation, control, onComplete); + return match.Subscribe(subject); } /// @@ -96,11 +97,8 @@ namespace Avalonia.Animation var lastFrameData = (lastCue.GetTypedValue(), lastCue.isNeutral); return (intraframeTime, new KeyFramePair(firstFrameData, lastFrameData)); } - - /// - /// Runs the KeyFrames Animation. - /// - private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete) + + internal IDisposable Run(Animation animation, Animatable control, Action onComplete) { var instance = new AnimationInstance(animation, control, this, onComplete, DoInterpolation); return control.Bind((AvaloniaProperty)Property, instance, BindingPriority.Animation); diff --git a/src/Avalonia.Animation/DisposeAnimationInstanceObservable.cs b/src/Avalonia.Animation/DisposeAnimationInstanceObservable.cs new file mode 100644 index 0000000000..902a09030b --- /dev/null +++ b/src/Avalonia.Animation/DisposeAnimationInstanceObservable.cs @@ -0,0 +1,62 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using Avalonia.Animation.Utils; +using Avalonia.Collections; +using Avalonia.Data; +using Avalonia.Reactive; + +namespace Avalonia.Animation +{ + /// + /// Manages the lifetime of animation instances as determined by its selector state. + /// + internal class DisposeAnimationInstanceObservable : IObserver, IDisposable + { + private IDisposable _lastInstance; + private bool _lastMatch; + private Animator _animator; + private Animation _animation; + private Animatable _control; + private Action _onComplete; + + public DisposeAnimationInstanceObservable(Animator animator, Animation animation, Animatable control, Action onComplete) + { + this._animator = animator; + this._animation = animation; + this._control = control; + this._onComplete = onComplete; + } + + public void Dispose() + { + _lastInstance?.Dispose(); + } + + public void OnCompleted() + { + } + + public void OnError(Exception error) + { + _lastInstance?.Dispose(); + } + + void IObserver.OnNext(bool matchVal) + { + if (matchVal != _lastMatch) + { + _lastInstance?.Dispose(); + if (matchVal) + { + _lastInstance = _animator.RunAnimation(_animation, _control, _onComplete); + } + _lastMatch = matchVal; + } + } + } +} \ No newline at end of file diff --git a/src/Avalonia.Base/Reactive/DisposeOnNextObservable.cs b/src/Avalonia.Base/Reactive/DisposeOnNextObservable.cs deleted file mode 100644 index 18af9e8752..0000000000 --- a/src/Avalonia.Base/Reactive/DisposeOnNextObservable.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using Avalonia.Threading; - -namespace Avalonia.Reactive -{ - public class DisposeOnNextObservable : LightweightObservableBase, IObserver where T : IDisposable - { - private IDisposable lastValue; - - private void ValueNext(T value) - { - lastValue?.Dispose(); - lastValue = value; - this.PublishNext(value); - } - - public void OnCompleted() - { - this.PublishCompleted(); - } - - public void OnError(Exception error) - { - this.PublishError(error); - } - - void IObserver.OnNext(T value) - { - ValueNext(value); - } - - protected override void Initialize() - { - } - - protected override void Deinitialize() - { - } - } -} \ No newline at end of file diff --git a/src/Avalonia.Base/Reactive/ObservableEx.cs b/src/Avalonia.Base/Reactive/ObservableEx.cs index dc3be36015..a1ec8f9a8a 100644 --- a/src/Avalonia.Base/Reactive/ObservableEx.cs +++ b/src/Avalonia.Base/Reactive/ObservableEx.cs @@ -21,21 +21,7 @@ namespace Avalonia.Reactive { return new SingleValueImpl(value); } - - /// - /// Disposes the current and saves the next. - /// - /// The type of the value. - /// The source . - /// The observable. - public static IObservable DisposeCurrentOnNext(this IObservable observable) - where T : IDisposable - { - var subject = new DisposeOnNextObservable(); - observable.Subscribe(subject); - return subject; - } - + private class SingleValueImpl : IObservable { private T _value;