Browse Source

Change completion to be notified via a callback. Only notify that an animation is complete if all animators used in the animation are complete.

pull/1774/head
Jeremy Koritzinsky 8 years ago
parent
commit
2345818aab
  1. 57
      src/Avalonia.Animation/Animation.cs
  2. 6
      src/Avalonia.Animation/AnimatorStateMachine`1.cs
  3. 9
      src/Avalonia.Animation/Animator`1.cs
  4. 4
      src/Avalonia.Animation/IAnimation.cs
  5. 2
      src/Avalonia.Animation/IAnimator.cs
  6. 6
      src/Avalonia.Visuals/Animation/TransformAnimator.cs

57
src/Avalonia.Animation/Animation.cs

@ -43,7 +43,6 @@ namespace Avalonia.Animation
return null;
}
private bool _isChildrenChanged = false;
private List<IDisposable> _subscription = new List<IDisposable>();
public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>();
@ -77,16 +76,6 @@ namespace Avalonia.Animation
/// </summary>
public Easing Easing { get; set; } = new LinearEasing();
/// <summary>
/// Triggers when the animation is completed.
/// </summary>
public event EventHandler Done;
public Animation()
{
this.CollectionChanged += delegate { _isChildrenChanged = true; };
}
private IList<IAnimator> InterpretKeyframes(Animatable control)
{
var handlerList = new List<(Type type, AvaloniaProperty property)>();
@ -152,11 +141,32 @@ namespace Avalonia.Animation
}
/// <inheritdocs/>
public IDisposable Apply(Animatable control, IObservable<bool> matchObs)
public IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete)
{
foreach (IAnimator animator in InterpretKeyframes(control))
var animators = InterpretKeyframes(control);
if (animators.Count == 1)
{
_subscription.Add(animator.Apply(this, control, matchObs));
_subscription.Add(animators[0].Apply(this, control, match, onComplete));
}
else
{
var completionTasks = onComplete != null ? new List<Task>() : null;
foreach (IAnimator animator in InterpretKeyframes(control))
{
Action animatorOnComplete = null;
if (onComplete != null)
{
var tcs = new TaskCompletionSource<object>();
animatorOnComplete = () => tcs.SetResult(null);
completionTasks.Add(tcs.Task);
}
_subscription.Add(animator.Apply(this, control, match, animatorOnComplete));
}
if (onComplete != null)
{
Task.WhenAll(completionTasks).ContinueWith(_ => onComplete());
}
}
return this;
}
@ -169,26 +179,9 @@ namespace Avalonia.Animation
if (this.RepeatCount == RepeatCount.Loop)
run.SetException(new InvalidOperationException("Looping animations must not use the Run method."));
EventHandler doneCallback = null;
doneCallback = (sender, args) =>
{
if (sender == control)
{
run.SetResult(null);
this.Done -= doneCallback;
}
};
this.Done += doneCallback;
this.Apply(control, Observable.Return(true));
this.Apply(control, Observable.Return(true), () => run.SetResult(null));
return run.Task;
}
internal void SetDone(Animatable control)
{
Done?.Invoke(control, null);
}
}
}

6
src/Avalonia.Animation/AnimatorStateMachine`1.cs

@ -35,6 +35,7 @@ namespace Avalonia.Animation
private T _neutralValue;
internal bool _unsubscribe = false;
private IObserver<object> _targetObserver;
private readonly Action _onComplete;
[Flags]
private enum KeyFramesStates
@ -51,7 +52,7 @@ namespace Avalonia.Animation
Disposed
}
public void Initialize(Animation animation, Animatable control, Animator<T> animator)
public AnimatorStateMachine(Animation animation, Animatable control, Animator<T> animator, Action onComplete)
{
_parent = animator;
_targetAnimation = animation;
@ -82,6 +83,7 @@ namespace Avalonia.Animation
_currentState = KeyFramesStates.DoDelay;
else
_currentState = KeyFramesStates.DoRun;
_onComplete = onComplete;
}
public void Step(PlayState _playState, Func<double, T, T> Interpolator)
@ -245,7 +247,7 @@ namespace Avalonia.Animation
}
_targetObserver.OnCompleted();
_targetAnimation.SetDone(_targetControl);
_onComplete?.Invoke();
Dispose();
handled = true;
break;

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

@ -35,7 +35,7 @@ namespace Avalonia.Animation
}
/// <inheritdoc/>
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch)
public virtual IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete)
{
if (!_isVerfifiedAndConverted)
VerifyConvertKeyFrames();
@ -45,7 +45,7 @@ namespace Avalonia.Animation
.Where(p => p && Timing.GetGlobalPlayState() != PlayState.Pause)
.Subscribe(_ =>
{
var timerObs = RunKeyFrames(animation, control);
var timerObs = RunKeyFrames(animation, control, onComplete);
});
}
@ -97,10 +97,9 @@ namespace Avalonia.Animation
/// <summary>
/// Runs the KeyFrames Animation.
/// </summary>
private IDisposable RunKeyFrames(Animation animation, Animatable control)
private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete)
{
var stateMachine = new AnimatorStateMachine<T>();
stateMachine.Initialize(animation, control, this);
var stateMachine = new AnimatorStateMachine<T>(animation, control, this, onComplete);
Timing.AnimationStateTimer
.TakeWhile(_ => !stateMachine._unsubscribe)

4
src/Avalonia.Animation/IAnimation.cs

@ -13,11 +13,11 @@ namespace Avalonia.Animation
/// <summary>
/// Apply the animation to the specified control
/// </summary>
IDisposable Apply(Animatable control, IObservable<bool> match);
IDisposable Apply(Animatable control, IObservable<bool> match, Action onComplete = null);
/// <summary>
/// Run the animation to the specified control
/// </summary>
Task RunAsync(Animatable control);
}
}
}

2
src/Avalonia.Animation/IAnimator.cs

@ -17,6 +17,6 @@ namespace Avalonia.Animation
/// <summary>
/// Applies the current KeyFrame group to the specified control.
/// </summary>
IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch);
IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete);
}
}

6
src/Avalonia.Visuals/Animation/TransformAnimator.cs

@ -19,7 +19,7 @@ namespace Avalonia.Animation
DoubleAnimator childKeyFrames;
/// <inheritdoc/>
public override IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch)
public override IDisposable Apply(Animation animation, Animatable control, IObservable<bool> obsMatch, Action onComplete)
{
var ctrl = (Visual)control;
@ -51,7 +51,7 @@ namespace Avalonia.Animation
// It's a transform object so let's target that.
if (renderTransformType == Property.OwnerType)
{
return childKeyFrames.Apply(animation, ctrl.RenderTransform, obsMatch);
return childKeyFrames.Apply(animation, ctrl.RenderTransform, obsMatch, onComplete);
}
// It's a TransformGroup and try finding the target there.
else if (renderTransformType == typeof(TransformGroup))
@ -60,7 +60,7 @@ namespace Avalonia.Animation
{
if (transform.GetType() == Property.OwnerType)
{
return childKeyFrames.Apply(animation, transform, obsMatch);
return childKeyFrames.Apply(animation, transform, obsMatch, onComplete);
}
}
}

Loading…
Cancel
Save