|
|
|
@ -1,4 +1,6 @@ |
|
|
|
using System; |
|
|
|
using System.Linq; |
|
|
|
using Avalonia.Data; |
|
|
|
|
|
|
|
namespace Avalonia.Animation.Keyframes |
|
|
|
{ |
|
|
|
@ -7,9 +9,8 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
/// </summary>
|
|
|
|
internal class KeyFramesStateMachine<T> : IObservable<object>, IDisposable |
|
|
|
{ |
|
|
|
|
|
|
|
T _lastInterpValue = default(T); |
|
|
|
|
|
|
|
T _firstKFValue = default(T); |
|
|
|
private ulong _delayTotalFrameCount, |
|
|
|
_durationTotalFrameCount, |
|
|
|
_delayFrameCount, |
|
|
|
@ -17,10 +18,16 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
_repeatCount, |
|
|
|
_currentIteration; |
|
|
|
|
|
|
|
private bool _isLooping, _isRepeating, _isReversed, _checkLoopAndRepeat; |
|
|
|
private bool _isLooping, |
|
|
|
_isRepeating, |
|
|
|
_isReversed, |
|
|
|
_checkLoopAndRepeat, |
|
|
|
_gotFirstKFValue; |
|
|
|
private FillMode _fillMode; |
|
|
|
private PlaybackDirection _animationDirection; |
|
|
|
KeyFramesStates _currentState, _savedState; |
|
|
|
private Animation _parentAnimation; |
|
|
|
private KeyFramesStates _currentState, _savedState; |
|
|
|
private KeyFrames<T> _parent; |
|
|
|
private Animation _targetAnimation; |
|
|
|
private Animatable _targetControl; |
|
|
|
internal bool _unsubscribe = false; |
|
|
|
double _outputTime = 0d; |
|
|
|
@ -40,9 +47,10 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
DISPOSED |
|
|
|
} |
|
|
|
|
|
|
|
public void Initialize(Animation animation, Animatable control) |
|
|
|
public void Initialize(Animation animation, Animatable control, KeyFrames<T> keyframes) |
|
|
|
{ |
|
|
|
_parentAnimation = animation; |
|
|
|
_parent = keyframes; |
|
|
|
_targetAnimation = animation; |
|
|
|
_targetControl = control; |
|
|
|
|
|
|
|
_delayTotalFrameCount = (ulong)(animation.Delay.Ticks / Timing.FrameTick.Ticks); |
|
|
|
@ -85,14 +93,14 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
_animationDirection = _parentAnimation.PlaybackDirection; |
|
|
|
_animationDirection = _targetAnimation.PlaybackDirection; |
|
|
|
_fillMode = _targetAnimation.FillMode; |
|
|
|
|
|
|
|
if (_durationTotalFrameCount > 0) |
|
|
|
_currentState = KeyFramesStates.DO_DELAY; |
|
|
|
else |
|
|
|
_currentState = KeyFramesStates.DO_RUN; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public void Step(PlayState _playState, Func<double, T> Interpolator) |
|
|
|
@ -109,6 +117,13 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
|
|
|
|
private void InternalStep(PlayState _playState, Func<double, T> Interpolator) |
|
|
|
{ |
|
|
|
|
|
|
|
if (!_gotFirstKFValue) |
|
|
|
{ |
|
|
|
_firstKFValue = (T)_parent.First().Value; |
|
|
|
_gotFirstKFValue = true; |
|
|
|
} |
|
|
|
|
|
|
|
if (_currentState == KeyFramesStates.DISPOSED) |
|
|
|
throw new InvalidProgramException("This KeyFrames Animation is already disposed."); |
|
|
|
|
|
|
|
@ -132,22 +147,34 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
switch (_currentState) |
|
|
|
{ |
|
|
|
case KeyFramesStates.DO_DELAY: |
|
|
|
|
|
|
|
if (_currentIteration == 0 |
|
|
|
&& _fillMode == FillMode.Backward |
|
|
|
|| _fillMode == FillMode.Both) |
|
|
|
{ |
|
|
|
_targetObserver.OnNext(_firstKFValue); |
|
|
|
} |
|
|
|
|
|
|
|
if (_delayFrameCount > _delayTotalFrameCount) |
|
|
|
{ |
|
|
|
_currentState = KeyFramesStates.DO_RUN; |
|
|
|
goto checkstate; |
|
|
|
} |
|
|
|
_delayFrameCount++; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case KeyFramesStates.DO_RUN: |
|
|
|
|
|
|
|
if (_isReversed) |
|
|
|
_currentState = KeyFramesStates.RUN_BACKWARDS; |
|
|
|
else |
|
|
|
_currentState = KeyFramesStates.RUN_FORWARDS; |
|
|
|
|
|
|
|
goto checkstate; |
|
|
|
|
|
|
|
case KeyFramesStates.RUN_FORWARDS: |
|
|
|
|
|
|
|
if (_durationFrameCount > _durationTotalFrameCount) |
|
|
|
{ |
|
|
|
_currentState = KeyFramesStates.RUN_COMPLETE; |
|
|
|
@ -160,6 +187,7 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
goto checkstate; |
|
|
|
|
|
|
|
case KeyFramesStates.RUN_BACKWARDS: |
|
|
|
|
|
|
|
if (_durationFrameCount > _durationTotalFrameCount) |
|
|
|
{ |
|
|
|
_currentState = KeyFramesStates.RUN_COMPLETE; |
|
|
|
@ -173,7 +201,7 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
|
|
|
|
case KeyFramesStates.RUN_APPLYVALUE: |
|
|
|
|
|
|
|
_easedTime = _parentAnimation.Easing.Ease(_tempDuration); |
|
|
|
_easedTime = _targetAnimation.Easing.Ease(_tempDuration); |
|
|
|
|
|
|
|
_durationFrameCount++; |
|
|
|
_lastInterpValue = Interpolator(_easedTime); |
|
|
|
@ -217,6 +245,11 @@ namespace Avalonia.Animation.Keyframes |
|
|
|
goto checkstate; |
|
|
|
|
|
|
|
case KeyFramesStates.STOP: |
|
|
|
if (_fillMode == FillMode.Forward |
|
|
|
|| _fillMode == FillMode.Both) |
|
|
|
{ |
|
|
|
_targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.Animation); |
|
|
|
} |
|
|
|
_targetObserver.OnCompleted(); |
|
|
|
break; |
|
|
|
} |
|
|
|
|