@ -7,21 +7,24 @@ namespace Avalonia.Animation.Keyframes
/// </summary>
internal class KeyFramesStateMachine < T > : IObservable < object > , IDisposable
{
ulong _d elayTotalFrameCount ,
T _l astInterpValue = default ( T ) ;
private ulong _d elayTotalFrameCount ,
_d urationTotalFrameCount ,
_d elayFrameCount ,
_d urationFrameCount ,
_ repeatCount ,
_ currentIteration ,
_ totalIteration ;
_ currentIteration ;
bool _ isLooping , _ isRepeating , _ isReversed ;
private bool _ isLooping , _ isRepeating , _ isReversed , _ checkLoopAndRepeat ;
private PlaybackDirection _ animationDirection ;
KeyFramesStates _ currentState , _ savedState ;
private Animation parentAnimation ;
private Animation _ parentAnimation ;
private Animatable _ targetControl ;
internal bool _ unsubscribe = false ;
double _ outputTime = 0d ;
private IObserver < object > targetObserver ;
private IObserver < object > _ targetObserver ;
private enum KeyFramesStates
{
@ -30,26 +33,28 @@ namespace Avalonia.Animation.Keyframes
DO_RUN ,
RUN_FORWARDS ,
RUN_BACKWARDS ,
RUN_APPLYVALUE ,
RUN_COMPLETE ,
PAUSE ,
STOP ,
DISPOSED
}
public void Start ( Animation animation )
public void Initialize ( Animation animation , Animatable control )
{
parentAnimation = animation ;
_ parentAnimation = animation ;
_ targetControl = control ;
_d elayTotalFrameCount = ( ulong ) ( animation . Delay . Ticks / Timing . FrameTick . Ticks ) ;
_d urationTotalFrameCount = ( ulong ) ( animation . Duration . Ticks / Timing . FrameTick . Ticks ) ;
switch ( animation . RepeatBehavior )
{
case RepeatBehavior . Loop :
_ isLooping = true ;
_ checkLoopAndRepeat = true ;
break ;
case RepeatBehavior . Repeat :
_ isRepeating = true ;
if ( animation . RepeatCount ! = null )
{
if ( animation . RepeatCount = = 0 )
@ -57,6 +62,8 @@ namespace Avalonia.Animation.Keyframes
throw new InvalidOperationException
( $"RepeatCount should be greater than zero when RepeatBehavior is set to Repeat." ) ;
}
_ isRepeating = true ;
_ checkLoopAndRepeat = true ;
_ repeatCount = ( ulong ) animation . RepeatCount ;
}
else
@ -78,13 +85,29 @@ namespace Avalonia.Animation.Keyframes
break ;
}
_ animationDirection = _ parentAnimation . PlaybackDirection ;
if ( _d urationTotalFrameCount > 0 )
_ currentState = KeyFramesStates . DO_DELAY ;
else
_ currentState = KeyFramesStates . DO_RUN ;
}
public void Step ( PlayState _ playState , Func < double , T > Interpolator )
{
try
{
InternalStep ( _ playState , Interpolator ) ;
}
catch ( Exception e )
{
_ targetObserver ? . OnError ( e ) ;
}
}
private void InternalStep ( PlayState _ playState , Func < double , T > Interpolator )
{
if ( _ currentState = = KeyFramesStates . DISPOSED )
throw new InvalidProgramException ( "This KeyFrames Animation is already disposed." ) ;
@ -103,7 +126,9 @@ namespace Avalonia.Animation.Keyframes
if ( _ playState ! = PlayState . Pause & & _ currentState = = KeyFramesStates . PAUSE )
_ currentState = _ savedState ;
checkstate :
double _ tempDuration = 0d , _ easedTime ;
checkstate :
switch ( _ currentState )
{
case KeyFramesStates . DO_DELAY :
@ -116,32 +141,83 @@ namespace Avalonia.Animation.Keyframes
break ;
case KeyFramesStates . DO_RUN :
// temporary stuff.
_ currentState = KeyFramesStates . RUN_FORWARDS ;
if ( _ isReversed )
_ currentState = KeyFramesStates . RUN_BACKWARDS ;
else
_ currentState = KeyFramesStates . RUN_FORWARDS ;
goto checkstate ;
case KeyFramesStates . RUN_FORWARDS :
// temporary stuff.
if ( _d urationFrameCount > _d urationTotalFrameCount )
{
_ currentState = KeyFramesStates . RUN_COMPLETE ;
goto checkstate ;
}
var tmp1 = ( double ) _d urationFrameCount / _d urationTotalFrameCount ;
var easedTime = parentAnimation . Easing . Ease ( tmp1 ) ;
_d urationFrameCount + + ;
_ tempDuration = ( double ) _d urationFrameCount / _d urationTotalFrameCount ;
_ currentState = KeyFramesStates . RUN_APPLYVALUE ;
targetObserver . OnNext ( Interpolator ( easedTime ) ) ;
break ;
goto checkstate ;
case KeyFramesStates . RUN_BACKWARDS :
// break;
if ( _d urationFrameCount > _d urationTotalFrameCount )
{
_ currentState = KeyFramesStates . RUN_COMPLETE ;
goto checkstate ;
}
_ tempDuration = ( double ) ( _d urationTotalFrameCount - _d urationFrameCount ) / _d urationTotalFrameCount ;
_ currentState = KeyFramesStates . RUN_APPLYVALUE ;
goto checkstate ;
case KeyFramesStates . RUN_APPLYVALUE :
_ easedTime = _ parentAnimation . Easing . Ease ( _ tempDuration ) ;
_d urationFrameCount + + ;
_l astInterpValue = Interpolator ( _ easedTime ) ;
_ targetObserver . OnNext ( _l astInterpValue ) ;
_ currentState = KeyFramesStates . DO_RUN ;
break ;
case KeyFramesStates . RUN_COMPLETE :
// break;
if ( _ checkLoopAndRepeat )
{
_d elayFrameCount = 0 ;
_d urationFrameCount = 0 ;
if ( _ isLooping )
{
_ currentState = KeyFramesStates . DO_RUN ;
}
else if ( _ isRepeating )
{
if ( _ currentIteration > = _ repeatCount )
{
_ currentState = KeyFramesStates . STOP ;
}
else
{
_ currentState = KeyFramesStates . DO_RUN ;
}
_ currentIteration + + ;
}
if ( _ animationDirection = = PlaybackDirection . Alternate
| | _ animationDirection = = PlaybackDirection . AlternateReverse )
_ isReversed = ! _ isReversed ;
break ;
}
_ currentState = KeyFramesStates . STOP ;
goto checkstate ;
case KeyFramesStates . STOP :
this . Dispose ( ) ;
_ targetObserver . OnCompleted ( ) ;
break ;
}
@ -149,16 +225,13 @@ namespace Avalonia.Animation.Keyframes
public IDisposable Subscribe ( IObserver < object > observer )
{
this . targetObserver = observer ;
_ targetObserver = observer ;
return this ;
}
public void Dispose ( )
{
targetObserver . OnCompleted ( ) ;
_ unsubscribe = true ;
_ currentState = KeyFramesStates . DISPOSED ;
}
}
}