Browse Source

Fix nits & polish up some code

pull/1793/head
Jumar Macato 8 years ago
parent
commit
8a800cadfa
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 25
      src/Avalonia.Animation/Animatable.cs
  2. 2
      src/Avalonia.Animation/Animation.cs
  3. 208
      src/Avalonia.Animation/AnimationsEngine`1.cs
  4. 11
      src/Avalonia.Animation/Animator`1.cs
  5. 30
      src/Avalonia.Animation/TransitionsEngine.cs

25
src/Avalonia.Animation/Animatable.cs

@ -14,15 +14,7 @@ namespace Avalonia.Animation
/// Base class for control which can have property transitions.
/// </summary>
public class Animatable : AvaloniaObject
{
/// <summary>
/// Initializes this <see cref="Animatable"/> object.
/// </summary>
public Animatable()
{
Transitions = new Transitions();
}
{
/// <summary>
/// Defines the <see cref="PlayState"/> property.
/// </summary>
@ -42,27 +34,25 @@ namespace Avalonia.Animation
{
get { return _playState; }
set { SetAndRaise(PlayStateProperty, ref _playState, value); }
}
/// <summary>
/// Defines the <see cref="Transitions"/> property.
/// </summary>
public static readonly DirectProperty<Animatable, IEnumerable<ITransition>> TransitionsProperty =
AvaloniaProperty.RegisterDirect<Animatable, IEnumerable<ITransition>>(
public static readonly DirectProperty<Animatable, Transitions> TransitionsProperty =
AvaloniaProperty.RegisterDirect<Animatable, Transitions>(
nameof(Transitions),
o => o.Transitions,
(o, v) => o.Transitions = v);
private IEnumerable<ITransition> _transitions = new AvaloniaList<ITransition>();
private Transitions _transitions;
/// <summary>
/// Gets or sets the property transitions for the control.
/// </summary>
public IEnumerable<ITransition> Transitions
public Transitions Transitions
{
get { return _transitions; }
get { return _transitions ?? (_transitions = new Transitions()); }
set { SetAndRaise(TransitionsProperty, ref _transitions, value); }
}
@ -83,6 +73,5 @@ namespace Avalonia.Animation
}
}
}
}
}
}

2
src/Avalonia.Animation/Animation.cs

@ -22,8 +22,6 @@ namespace Avalonia.Animation
/// </summary>
public static PlayState GlobalPlayState { get; set; } = PlayState.Run;
public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>();
/// <summary>
/// Gets or sets the active time of this animation.
/// </summary>

208
src/Avalonia.Animation/AnimationsEngine`1.cs

@ -1,7 +1,9 @@
using System;
using System.Linq;
using System.Reactive.Linq;
using Avalonia.Animation.Utils;
using Avalonia.Data;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
@ -9,182 +11,184 @@ namespace Avalonia.Animation
/// Handles interpolatoin and time-related functions
/// for keyframe animations.
/// </summary>
internal class AnimationsEngine<T> : IObservable<T>, IDisposable
internal class AnimationsEngine<T> : SingleSubscriberObservableBase<T>
{
T lastInterpValue;
T firstKFValue;
private long repeatCount;
private double currentIteration;
private bool isLooping;
private bool gotFirstKFValue;
private bool gotFirstFrameCount;
private bool iterationDelay;
private FillMode fillMode;
private PlaybackDirection animationDirection;
private Animator<T> parent;
private Animatable targetControl;
private T neutralValue;
private double speedRatio;
internal bool unsubscribe;
private bool isDisposed;
private TimeSpan delay;
private TimeSpan duration;
private TimeSpan firstFrameCount;
private TimeSpan internalClock;
private TimeSpan? previousClock;
private Easings.Easing easeFunc;
private IObserver<T> targetObserver;
private readonly Action onCompleteAction;
public AnimationsEngine(Animation animation, Animatable control, Animator<T> animator, Action OnComplete)
private T _lastInterpValue;
private T _firstKFValue;
private long _repeatCount;
private double _currentIteration;
private bool _isLooping;
private bool _gotFirstKFValue;
private bool _gotFirstFrameCount;
private bool _iterationDelay;
private FillMode _fillMode;
private PlaybackDirection _animationDirection;
private Animator<T> _parent;
private Animatable _targetControl;
private T _neutralValue;
private double _speedRatio;
private TimeSpan _delay;
private TimeSpan _duration;
private TimeSpan _firstFrameCount;
private TimeSpan _internalClock;
private TimeSpan? _previousClock;
private Easings.Easing _easeFunc;
private Action _onCompleteAction;
private Func<double, T, T> _interpolator;
private IDisposable _timerSubscription;
public AnimationsEngine(Animation animation, Animatable control, Animator<T> animator, Action OnComplete, Func<double, T, T> Interpolator)
{
if (animation.SpeedRatio <= 0 || DoubleUtils.AboutEqual(animation.SpeedRatio, 0))
throw new InvalidOperationException("Speed ratio cannot be negative or zero.");
if (animation.Duration.TotalSeconds <= 0 || DoubleUtils.AboutEqual(animation.Duration.TotalSeconds, 0))
throw new InvalidOperationException("Duration cannot be negative or zero.");
parent = animator;
easeFunc = animation.Easing;
targetControl = control;
neutralValue = (T)targetControl.GetValue(parent.Property);
speedRatio = animation.SpeedRatio;
_parent = animator;
_easeFunc = animation.Easing;
_targetControl = control;
_neutralValue = (T)_targetControl.GetValue(_parent.Property);
_speedRatio = animation.SpeedRatio;
delay = animation.Delay;
duration = animation.Duration;
iterationDelay = animation.DelayBetweenIterations;
_delay = animation.Delay;
_duration = animation.Duration;
_iterationDelay = animation.DelayBetweenIterations;
switch (animation.RepeatCount.RepeatType)
{
case RepeatType.None:
repeatCount = 1;
_repeatCount = 1;
break;
case RepeatType.Loop:
isLooping = true;
_isLooping = true;
break;
case RepeatType.Repeat:
repeatCount = (long)animation.RepeatCount.Value;
_repeatCount = (long)animation.RepeatCount.Value;
break;
}
animationDirection = animation.PlaybackDirection;
fillMode = animation.FillMode;
onCompleteAction = OnComplete;
_animationDirection = animation.PlaybackDirection;
_fillMode = animation.FillMode;
_onCompleteAction = OnComplete;
_interpolator = Interpolator;
}
protected override void Unsubscribed()
{
_timerSubscription?.Dispose();
}
public void Step(TimeSpan frameTick, Func<double, T, T> Interpolator)
protected override void Subscribed()
{
_timerSubscription = Timing.AnimationsTimer
.Subscribe(p => this.Step(p));
}
public void Step(TimeSpan frameTick)
{
try
{
InternalStep(frameTick, Interpolator);
InternalStep(frameTick);
}
catch (Exception e)
{
targetObserver?.OnError(e);
PublishError(e);
}
}
private void DoComplete()
{
if (fillMode == FillMode.Forward || fillMode == FillMode.Both)
targetControl.SetValue(parent.Property, lastInterpValue, BindingPriority.LocalValue);
if (_fillMode == FillMode.Forward || _fillMode == FillMode.Both)
_targetControl.SetValue(_parent.Property, _lastInterpValue, BindingPriority.LocalValue);
targetObserver.OnCompleted();
onCompleteAction?.Invoke();
Dispose();
_onCompleteAction?.Invoke();
PublishCompleted();
}
private void DoDelay()
{
if (fillMode == FillMode.Backward || fillMode == FillMode.Both)
if (currentIteration == 0)
targetObserver.OnNext(firstKFValue);
if (_fillMode == FillMode.Backward || _fillMode == FillMode.Both)
if (_currentIteration == 0)
PublishNext(_firstKFValue);
else
targetObserver.OnNext(lastInterpValue);
PublishNext(_lastInterpValue);
}
private void DoPlayStatesAndTime(TimeSpan systemTime)
{
if (Animation.GlobalPlayState == PlayState.Stop || targetControl.PlayState == PlayState.Stop)
if (Animation.GlobalPlayState == PlayState.Stop || _targetControl.PlayState == PlayState.Stop)
DoComplete();
if (!previousClock.HasValue)
if (!_previousClock.HasValue)
{
previousClock = systemTime;
internalClock = TimeSpan.Zero;
_previousClock = systemTime;
_internalClock = TimeSpan.Zero;
}
else
{
if (Animation.GlobalPlayState == PlayState.Pause || targetControl.PlayState == PlayState.Pause)
if (Animation.GlobalPlayState == PlayState.Pause || _targetControl.PlayState == PlayState.Pause)
{
previousClock = systemTime;
_previousClock = systemTime;
return;
}
var delta = systemTime - previousClock;
internalClock += delta.Value;
previousClock = systemTime;
var delta = systemTime - _previousClock;
_internalClock += delta.Value;
_previousClock = systemTime;
}
if (!gotFirstKFValue)
if (!_gotFirstKFValue)
{
firstKFValue = (T)parent.First().Value;
gotFirstKFValue = true;
_firstKFValue = (T)_parent.First().Value;
_gotFirstKFValue = true;
}
if (!gotFirstFrameCount)
if (!_gotFirstFrameCount)
{
firstFrameCount = internalClock;
gotFirstFrameCount = true;
_firstFrameCount = _internalClock;
_gotFirstFrameCount = true;
}
}
private void InternalStep(TimeSpan systemTime, Func<double, T, T> Interpolator)
private void InternalStep(TimeSpan systemTime)
{
DoPlayStatesAndTime(systemTime);
if (isDisposed)
throw new InvalidProgramException("This KeyFrames Animation is already disposed.");
var time = internalClock - firstFrameCount;
var delayEndpoint = delay;
var iterationEndpoint = delayEndpoint + duration;
var time = _internalClock - _firstFrameCount;
var delayEndpoint = _delay;
var iterationEndpoint = delayEndpoint + _duration;
//determine if time is currently in the first iteration.
if (time >= TimeSpan.Zero & time <= iterationEndpoint)
{
currentIteration = 1;
_currentIteration = 1;
}
else if (time > iterationEndpoint)
{
//Subtract first iteration to properly get the subsequent iteration time
time -= iterationEndpoint;
if (!iterationDelay & delayEndpoint > TimeSpan.Zero)
if (!_iterationDelay & delayEndpoint > TimeSpan.Zero)
{
delayEndpoint = TimeSpan.Zero;
iterationEndpoint = duration;
iterationEndpoint = _duration;
}
//Calculate the current iteration number
currentIteration = (int)Math.Floor((double)time.Ticks / iterationEndpoint.Ticks) + 2;
_currentIteration = (int)Math.Floor((double)time.Ticks / iterationEndpoint.Ticks) + 2;
}
else
{
previousClock = systemTime;
_previousClock = systemTime;
return;
}
time = TimeSpan.FromTicks(time.Ticks % iterationEndpoint.Ticks);
if (!isLooping)
if (!_isLooping)
{
if (currentIteration > repeatCount)
if (_currentIteration > _repeatCount)
DoComplete();
if (time > iterationEndpoint)
@ -192,10 +196,10 @@ namespace Avalonia.Animation
}
// Determine if the current iteration should have its normalized time inverted.
bool isCurIterReverse = animationDirection == PlaybackDirection.Normal ? false :
animationDirection == PlaybackDirection.Alternate ? (currentIteration % 2 == 0) ? false : true :
animationDirection == PlaybackDirection.AlternateReverse ? (currentIteration % 2 == 0) ? true : false :
animationDirection == PlaybackDirection.Reverse ? true : false;
bool isCurIterReverse = _animationDirection == PlaybackDirection.Normal ? false :
_animationDirection == PlaybackDirection.Alternate ? (_currentIteration % 2 == 0) ? false : true :
_animationDirection == PlaybackDirection.AlternateReverse ? (_currentIteration % 2 == 0) ? true : false :
_animationDirection == PlaybackDirection.Reverse ? true : false;
if (delayEndpoint > TimeSpan.Zero & time < delayEndpoint)
{
@ -214,23 +218,11 @@ namespace Avalonia.Animation
interpVal = 1 - interpVal;
// Ease and interpolate
var easedTime = easeFunc.Ease(interpVal);
lastInterpValue = Interpolator(easedTime, neutralValue);
var easedTime = _easeFunc.Ease(interpVal);
_lastInterpValue = _interpolator(easedTime, _neutralValue);
targetObserver.OnNext(lastInterpValue);
PublishNext(_lastInterpValue);
}
}
public IDisposable Subscribe(IObserver<T> observer)
{
targetObserver = observer;
return this;
}
public void Dispose()
{
unsubscribe = true;
isDisposed = true;
}
}
}

11
src/Avalonia.Animation/Animator`1.cs

@ -32,12 +32,12 @@ namespace Avalonia.Animation
}
/// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> Match, Action onComplete)
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> match, Action onComplete)
{
if (!_isVerifiedAndConverted)
VerifyConvertKeyFrames();
return Match
return match
.Where(p => p)
.Subscribe(_ =>
{
@ -103,12 +103,7 @@ namespace Avalonia.Animation
/// </summary>
private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete)
{
var stateMachine = new AnimationsEngine<T>(animation, control, this, onComplete);
Timing.AnimationsTimer
.TakeWhile(_ => !stateMachine.unsubscribe)
.Subscribe(p => stateMachine.Step(p, DoInterpolation));
var stateMachine = new AnimationsEngine<T>(animation, control, this, onComplete, DoInterpolation);
return control.Bind<T>((AvaloniaProperty<T>)Property, stateMachine, BindingPriority.Animation);
}

30
src/Avalonia.Animation/TransitionsEngine.cs

@ -6,12 +6,15 @@ using System;
using System.Reactive.Linq;
using Avalonia.Animation.Easings;
using Avalonia.Animation.Utils;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
public class TransitionsEngine : IObservable<double>, IDisposable
/// <summary>
/// Handles the timing and lifetime of a <see cref="Transition{T}"/>.
/// </summary>
public class TransitionsEngine : SingleSubscriberObservableBase<double>
{
private IObserver<double> observer;
private IDisposable timerSubscription;
private readonly TimeSpan startTime;
private readonly TimeSpan duration;
@ -20,10 +23,6 @@ namespace Avalonia.Animation
{
startTime = Timing.GetTickCount();
duration = Duration;
timerSubscription = Timing
.AnimationsTimer
.Subscribe(t => TimerTick(t));
}
private void TimerTick(TimeSpan t)
@ -33,26 +32,23 @@ namespace Avalonia.Animation
if (interpVal > 1d
|| interpVal < 0d)
{
this.Dispose();
PublishCompleted();
return;
}
observer?.OnNext(interpVal);
PublishNext(interpVal);
}
public void Dispose()
protected override void Unsubscribed()
{
timerSubscription?.Dispose();
observer?.OnCompleted();
}
public IDisposable Subscribe(IObserver<double> Observer)
protected override void Subscribed()
{
if (Observer is null)
throw new InvalidProgramException("Can only set the subscription once.");
observer = Observer;
return this;
timerSubscription = Timing
.AnimationsTimer
.Subscribe(t => TimerTick(t));
}
}
}
Loading…
Cancel
Save