1
0
Эх сурвалжийг харах

Make KeyFrameStateMachine an IObservable.

Jumar Macato 7 жил өмнө
parent
commit
1acfc74fb1

+ 11 - 13
src/Avalonia.Animation/Keyframes/DoubleKeyFrames.cs

@@ -14,21 +14,19 @@ namespace Avalonia.Animation.Keyframes
     /// </summary>
     public class DoubleKeyFrames : KeyFrames<double>
     {
-        /// <inheritdocs/>
-        public override IObservable<double> DoInterpolation(IObservable<double> timer, Animation animation, Animatable control)
-          => timer.Select(t =>
-             {
-                  // Get a pair of keyframes to make the interpolation.
-                  var pair = GetKeyFramePairByTime(t);
 
-                 double y0 = pair.FirstKeyFrame.Value;
-                 double t0 = pair.FirstKeyFrame.Key;
-                 double y1 = pair.SecondKeyFrame.Value;
-                 double t1 = pair.SecondKeyFrame.Key;
+        /// <inheritdocs/>
+        public override double DoInterpolation(double t)
+        {
+            var pair = GetKeyFramePairByTime(t);
 
-                  // Do linear parametric interpolation 
-                  return y0 + ((t - t0) / (t1 - t0)) * (y1 - y0);
-             });
+            double y0 = pair.FirstKeyFrame.Value;
+            double t0 = pair.FirstKeyFrame.Key;
+            double y1 = pair.SecondKeyFrame.Value;
+            double t1 = pair.SecondKeyFrame.Key;
 
+            // Do linear parametric interpolation 
+            return y0 + ((t - t0) / (t1 - t0)) * (y1 - y0);
+        }
     }
 }

+ 22 - 24
src/Avalonia.Animation/Keyframes/KeyFrames.cs

@@ -39,14 +39,10 @@ namespace Avalonia.Animation.Keyframes
             return obsMatch
                 .Where(p => p == true)
                 // Ignore triggers when global timers are paused.
-                .Where(p => Timing.GetGlobalPlayState() == PlayState.Run)
+                .Where(p => Timing.GetGlobalPlayState() != PlayState.Pause)
                 .Subscribe(_ =>
                 {
-                    var timerObs = SetupAnimation(animation, control);
-                    var interp = DoInterpolation(timerObs, animation, control)
-                                .Select(p => (object)p);
-
-                    control.Bind(Property, interp, BindingPriority.Animation);
+                    var timerObs = RunKeyFrames(animation, control);
                 });
         }
 
@@ -86,33 +82,35 @@ namespace Avalonia.Animation.Keyframes
 
 
         /// <summary>
-        /// Returns an observable timer with the specific Animation
-        /// duration and delay and applies the Animation's easing function.
+        /// Runs the KeyFrames Animation.
         /// </summary>
-        public IObservable<double> SetupAnimation(Animation animation, Animatable control)
+        public IDisposable RunKeyFrames(Animation animation, Animatable control)
         {
-            var newStateMachine = new KeyFramesStateMachine();
-            newStateMachine.Start(animation, control);
-            var playStateObs = Timing.AnimationStateTimer
-                                     .TakeWhile(p => !newStateMachine._unsubscribe)
-                                     .Select(p =>
-                                     {
-                                         return newStateMachine.Step(p);
-                                     });
-            // var newTimer = Timing.GetAnimationsTimer(animation, control, animation.Duration, animation.Delay);
-            return playStateObs.Select(t => animation.Easing.Ease(t));
-        }
+            var _kfStateMach = new KeyFramesStateMachine<T>();
+            _kfStateMach.Start(animation);
+
+            Timing.AnimationStateTimer
+                         .TakeWhile(_ => !_kfStateMach._unsubscribe)
+                         .Subscribe(p =>
+                         {
+                             _kfStateMach.Step(p, DoInterpolation);
+                         });
 
+            return control.Bind(Property, _kfStateMach, BindingPriority.Animation);
+        }
 
         /// <summary>
-        /// Interpolates the given keyframes to the control.
+        /// Interpolates a value given the desired time.
         /// </summary>
-        public abstract IObservable<T> DoInterpolation(IObservable<double> timer, Animation animation,
-                                                       Animatable control);
+        public abstract T DoInterpolation(double time);
+
+
+        // public abstract IObservable<T> DoInterpolation(IObservable<double> timer, Animation animation,
+        //                                                Animatable control);
 
 
         /// <summary>
-        /// Verifies and converts keyframe values according to this class type parameter.
+        /// Verifies and converts keyframe values according to this class's type parameter.
         /// </summary>
         private void VerifyConvertKeyFrames(Animation animation, Type type)
         {

+ 54 - 36
src/Avalonia.Animation/Keyframes/KeyFramesStateMachine.cs

@@ -5,9 +5,8 @@ namespace Avalonia.Animation.Keyframes
     /// <summary>
     /// Provides statefulness for an iteration of a keyframe group animation.
     /// </summary>
-    internal class KeyFramesStateMachine
+    internal class KeyFramesStateMachine<T> : IObservable<object>, IDisposable
     {
-
         ulong _delayTotalFrameCount,
             _durationTotalFrameCount,
             _delayFrameCount,
@@ -18,11 +17,11 @@ namespace Avalonia.Animation.Keyframes
 
         bool _isLooping, _isRepeating, _isReversed;
         private PlaybackDirection _animationDirection;
-        KeyFramesStates _currentState;
-
+        KeyFramesStates _currentState, _savedState;
+        private Animation parentAnimation;
         internal bool _unsubscribe = false;
-        private Animation _parentAnimation;
-        private Animatable _targetAnimatable;
+        double _outputTime = 0d;
+        private IObserver<object> targetObserver;
 
         private enum KeyFramesStates
         {
@@ -32,21 +31,14 @@ namespace Avalonia.Animation.Keyframes
             RUN_FORWARDS,
             RUN_BACKWARDS,
             RUN_COMPLETE,
-            STOP
+            PAUSE,
+            STOP,
+            DISPOSED
         }
 
-        public void Start(Animation animation, Animatable control)
+        public void Start(Animation animation)
         {
-            this._parentAnimation = animation;
-            this._targetAnimatable = control;
-
-            // int _delayFrameCount,
-            //     _durationFrameCount,
-            //     _repeatCount,
-            //     _iterationDirection,
-            //     _currentIteration,
-            //     _totalIteration;
-
+            parentAnimation = animation;
             _delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks);
             _durationTotalFrameCount = (ulong)(animation.Duration.Ticks / Timing.FrameTick.Ticks);
 
@@ -78,37 +70,39 @@ namespace Avalonia.Animation.Keyframes
                     }
                     break;
             }
-            _animationDirection = animation.PlaybackDirection;
-            
-            switch (_animationDirection)
+
+            switch (animation.PlaybackDirection)
             {
                 case PlaybackDirection.Reverse:
                 case PlaybackDirection.AlternateReverse:
-                    SetInitialPlaybackDirection(true);
+                    _isReversed = true;
                     break;
                 default:
-                    SetInitialPlaybackDirection(false);
+                    _isReversed = false;
                     break;
             }
 
             _currentState = KeyFramesStates.DO_RUN;
         }
 
-        private void SetInitialPlaybackDirection(bool isReversed)
-        {
-            _isReversed = isReversed;
-        }
-
-        private bool GetPlaybackDirection()
+        public double Step(PlayState _playState, Func<double, T> Interpolator)
         {
-            _isReversed = !_isReversed;
-            return _isReversed;
-        }
+            if (_currentState == KeyFramesStates.DISPOSED) throw new InvalidProgramException("This KeyFrames Animation is already disposed.");
 
-        public double Step(PlayState _playState)
-        {
             if (_playState == PlayState.Stop) _currentState = KeyFramesStates.STOP;
 
+            // Save state and pause the machine
+            if (_playState == PlayState.Pause && _currentState != KeyFramesStates.PAUSE)
+            {
+                _savedState = _currentState;
+                _currentState = KeyFramesStates.PAUSE;
+            }
+
+            // Resume the previous state
+            if (_playState != PlayState.Pause && _currentState == KeyFramesStates.PAUSE)
+                _currentState = _savedState;
+
+            checkstate:
             switch (_currentState)
             {
                 case KeyFramesStates.DO_DELAY:
@@ -120,11 +114,22 @@ namespace Avalonia.Animation.Keyframes
                     return 0d;
 
                 case KeyFramesStates.DO_RUN:
+                    // temporary stuff.
+                    _currentState = KeyFramesStates.RUN_FORWARDS;
+
+                    goto checkstate;
 
-                    break;
 
                 case KeyFramesStates.RUN_FORWARDS:
-                // break;
+                    // temporary stuff.
+                    if (_durationFrameCount > _durationTotalFrameCount)
+                        _currentState = KeyFramesStates.RUN_COMPLETE;
+
+                    var tmp1 = (double)_durationFrameCount / _durationTotalFrameCount;
+                    var easedTime = parentAnimation.Easing.Ease(tmp1);
+                    _durationFrameCount++;
+                    targetObserver.OnNext(Interpolator(easedTime));
+                    break;
 
                 case KeyFramesStates.RUN_BACKWARDS:
                 // break;
@@ -133,11 +138,24 @@ namespace Avalonia.Animation.Keyframes
                 // break;
 
                 case KeyFramesStates.STOP:
+                    targetObserver.OnCompleted();
                     _unsubscribe = true;
                     break;
             }
             return 0;
         }
 
+        public IDisposable Subscribe(IObserver<object> observer)
+        {
+            this.targetObserver = observer;
+            return this;
+        }
+
+        public void Dispose()
+        {
+            _unsubscribe = true;
+            _currentState = KeyFramesStates.DISPOSED;
+            targetObserver = null;
+        }
     }
 }

+ 0 - 0
src/Avalonia.Animation/Direction.cs → src/Avalonia.Animation/PlaybackDirection.cs


+ 1 - 3
src/Avalonia.DotNetCoreRuntime/AppBuilder.cs

@@ -47,8 +47,6 @@ namespace Avalonia
             //that CLR doesn't try to load dependencies before referencing method is jitted
             if (os == OperatingSystemType.WinNT)
                 LoadWin32();
-            else if(os==OperatingSystemType.OSX)
-                LoadMonoMac();
             else
                 LoadGtk3();
             this.UseSkia();
@@ -56,7 +54,7 @@ namespace Avalonia
             return this;
         }
 
-        void LoadMonoMac() => this.UseMonoMac();
+        
         void LoadWin32() => this.UseWin32();
         void LoadGtk3() => this.UseGtk3();
     }

+ 1 - 3
src/Avalonia.Visuals/Animation/Keyframes/TransformKeyFrames.cs

@@ -73,8 +73,6 @@ namespace Avalonia.Animation.Keyframes
         }
 
         /// <inheritdocs/>
-        public override IObservable<double> DoInterpolation(IObservable<double> timer, Animation animation, Animatable control) => null;
-
-        
+        public override double DoInterpolation(double time) => 0;
     }
 }