Browse Source

Cleanup and refactor leftover transition types.

pull/5953/head
Dariusz Komosinski 5 years ago
parent
commit
21b7b88b0f
  1. 5
      src/Avalonia.Animation/AnimatorDrivenTransition.cs
  2. 9
      src/Avalonia.Animation/AnimatorTransitionObservable.cs
  3. 4
      src/Avalonia.Animation/Clock.cs
  4. 5
      src/Avalonia.Animation/ClockBase.cs
  5. 37
      src/Avalonia.Animation/TransitionInstance.cs
  6. 39
      src/Avalonia.Animation/TransitionObservableBase.cs
  7. 14
      src/Avalonia.Animation/Transitions/DoubleTransition.cs
  8. 13
      src/Avalonia.Animation/Transitions/FloatTransition.cs
  9. 13
      src/Avalonia.Animation/Transitions/IntegerTransition.cs
  10. 13
      src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs
  11. 12
      src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs
  12. 13
      src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs
  13. 13
      src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs
  14. 13
      src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs
  15. 13
      src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs
  16. 13
      src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs
  17. 37
      tests/Avalonia.Benchmarks/Animations/TransitionBenchmark.cs

5
src/Avalonia.Animation/AnimatorDrivenTransition.cs

@ -3,6 +3,11 @@ using Avalonia.Animation.Animators;
namespace Avalonia.Animation
{
/// <summary>
/// <see cref="Transition{T}"/> using an <see cref="Animator{T}"/> to transition between values.
/// </summary>
/// <typeparam name="T">Type of the transitioned value.</typeparam>
/// <typeparam name="TAnimator">Type of the animator.</typeparam>
public abstract class AnimatorDrivenTransition<T, TAnimator> : Transition<T> where TAnimator : Animator<T>, new()
{
private static readonly TAnimator s_animator = new TAnimator();

9
src/Avalonia.Animation/AnimatorTransitionObservable.cs

@ -4,6 +4,11 @@ using Avalonia.Animation.Easings;
namespace Avalonia.Animation
{
/// <summary>
/// Transition observable based on an <see cref="Animator{T}"/> producing a value.
/// </summary>
/// <typeparam name="T">Type of the transitioned value.</typeparam>
/// <typeparam name="TAnimator">Type of the animator.</typeparam>
public class AnimatorTransitionObservable<T, TAnimator> : TransitionObservableBase<T> where TAnimator : Animator<T>
{
private readonly TAnimator _animator;
@ -11,7 +16,7 @@ namespace Avalonia.Animation
private readonly T _oldValue;
private readonly T _newValue;
public AnimatorTransitionObservable(TAnimator animator, IObservable<double> progress, Easing easing, T oldValue, T newValue) : base(progress)
public AnimatorTransitionObservable(TAnimator animator, IObservable<double> progress, Easing easing, T oldValue, T newValue) : base(progress, easing)
{
_animator = animator;
_easing = easing;
@ -21,8 +26,6 @@ namespace Avalonia.Animation
protected override T ProduceValue(double progress)
{
progress = _easing.Ease(progress);
return _animator.Interpolate(progress, _oldValue, _newValue);
}
}

4
src/Avalonia.Animation/Clock.cs

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Reactive;
namespace Avalonia.Animation
{

5
src/Avalonia.Animation/ClockBase.cs

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Reactive.Linq;
using System.Text;
using Avalonia.Reactive;
namespace Avalonia.Animation
@ -58,7 +55,7 @@ namespace Avalonia.Animation
return _observable.Subscribe(observer);
}
private class ClockObservable : LightweightObservableBase<TimeSpan>
private sealed class ClockObservable : LightweightObservableBase<TimeSpan>
{
public bool HasSubscriptions { get; private set; }
public void Pulse(TimeSpan time) => PublishNext(time);

37
src/Avalonia.Animation/TransitionInstance.cs

@ -1,4 +1,5 @@
using System;
using System.Runtime.ExceptionServices;
using Avalonia.Reactive;
using Avalonia.Utilities;
@ -13,7 +14,7 @@ namespace Avalonia.Animation
private TimeSpan _delay;
private TimeSpan _duration;
private readonly IClock _baseClock;
private IClock _clock;
private TransitionClock _clock;
public TransitionInstance(IClock clock, TimeSpan delay, TimeSpan duration)
{
@ -71,7 +72,7 @@ namespace Avalonia.Animation
protected override void Subscribed()
{
_clock = new Clock(_baseClock);
_clock = new TransitionClock(_baseClock);
_timerSubscription = _clock.Subscribe(this);
PublishNext(0.0d);
}
@ -90,5 +91,37 @@ namespace Avalonia.Animation
{
TimerTick(value);
}
/// <summary>
/// TODO: This clock is still fairly expensive due to <see cref="ClockBase"/> implementation.
/// </summary>
private sealed class TransitionClock : ClockBase, IObserver<TimeSpan>
{
private readonly IDisposable _parentSubscription;
public TransitionClock(IClock parent)
{
_parentSubscription = parent.Subscribe(this);
}
protected override void Stop()
{
_parentSubscription.Dispose();
}
void IObserver<TimeSpan>.OnNext(TimeSpan value)
{
Pulse(value);
}
void IObserver<TimeSpan>.OnCompleted()
{
}
void IObserver<TimeSpan>.OnError(Exception error)
{
ExceptionDispatchInfo.Capture(error).Throw();
}
}
}
}

39
src/Avalonia.Animation/TransitionObservableBase.cs

@ -1,45 +1,58 @@
using System;
using Avalonia.Animation.Easings;
using Avalonia.Reactive;
#nullable enable
namespace Avalonia.Animation
{
/// <summary>
/// Provides base for observables implementing transitions.
/// </summary>
/// <typeparam name="T">Type of the transitioned value.</typeparam>
public abstract class TransitionObservableBase<T> : SingleSubscriberObservableBase<T>, IObserver<double>
{
private readonly Easing _easing;
private readonly IObservable<double> _progress;
private IDisposable? _progressSubscription;
protected TransitionObservableBase(IObservable<double> progress)
protected TransitionObservableBase(IObservable<double> progress, Easing easing)
{
_progress = progress;
_easing = easing;
}
void IObserver<double>.OnCompleted()
/// <summary>
/// Produces value at given progress time point.
/// </summary>
/// <param name="progress">Transition progress.</param>
protected abstract T ProduceValue(double progress);
protected override void Subscribed()
{
PublishCompleted();
_progressSubscription = _progress.Subscribe(this);
}
void IObserver<double>.OnError(Exception error)
protected override void Unsubscribed()
{
PublishError(error);
_progressSubscription?.Dispose();
}
void IObserver<double>.OnNext(double value)
void IObserver<double>.OnCompleted()
{
PublishNext(ProduceValue(value));
PublishCompleted();
}
protected override void Unsubscribed()
void IObserver<double>.OnError(Exception error)
{
_progressSubscription?.Dispose();
PublishError(error);
}
protected override void Subscribed()
void IObserver<double>.OnNext(double value)
{
_progressSubscription = _progress.Subscribe(this);
}
double progress = _easing.Ease(value);
protected abstract T ProduceValue(double progress);
PublishNext(ProduceValue(progress));
}
}
}

14
src/Avalonia.Animation/Transitions/DoubleTransition.cs

@ -1,5 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -10,16 +8,4 @@ namespace Avalonia.Animation
public class DoubleTransition : AnimatorDrivenTransition<double, DoubleAnimator>
{
}
public class DoubleTransitionOld : Transition<double>
{
private static readonly DoubleAnimator s_animator = new DoubleAnimator();
/// <inheritdocs/>
public override IObservable<double> DoTransition(IObservable<double> progress, double oldValue, double newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

13
src/Avalonia.Animation/Transitions/FloatTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -8,15 +5,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="float"/> types.
/// </summary>
public class FloatTransition : Transition<float>
public class FloatTransition : AnimatorDrivenTransition<float, FloatAnimator>
{
private static readonly FloatAnimator s_animator = new FloatAnimator();
/// <inheritdocs/>
public override IObservable<float> DoTransition(IObservable<double> progress, float oldValue, float newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

13
src/Avalonia.Animation/Transitions/IntegerTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -8,15 +5,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="int"/> types.
/// </summary>
public class IntegerTransition : Transition<int>
public class IntegerTransition : AnimatorDrivenTransition<int, Int32Animator>
{
private static readonly Int32Animator s_animator = new Int32Animator();
/// <inheritdocs/>
public override IObservable<int> DoTransition(IObservable<double> progress, int oldValue, int newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

13
src/Avalonia.Visuals/Animation/Transitions/BoxShadowsTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
using Avalonia.Media;
@ -9,15 +6,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="BoxShadows"/> type.
/// </summary>
public class BoxShadowsTransition : Transition<BoxShadows>
public class BoxShadowsTransition : AnimatorDrivenTransition<BoxShadows, BoxShadowsAnimator>
{
private static readonly BoxShadowsAnimator s_animator = new BoxShadowsAnimator();
/// <inheritdocs/>
public override IObservable<BoxShadows> DoTransition(IObservable<double> progress, BoxShadows oldValue, BoxShadows newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

12
src/Avalonia.Visuals/Animation/Transitions/ColorTransition.cs

@ -0,0 +1,12 @@
using Avalonia.Animation.Animators;
using Avalonia.Media;
namespace Avalonia.Animation
{
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Color"/> type.
/// </summary>
public class ColorTransition : AnimatorDrivenTransition<Color, ColorAnimator>
{
}
}

13
src/Avalonia.Visuals/Animation/Transitions/CornerRadiusTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -8,15 +5,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="CornerRadius"/> type.
/// </summary>
public class CornerRadiusTransition : Transition<CornerRadius>
public class CornerRadiusTransition : AnimatorDrivenTransition<CornerRadius, CornerRadiusAnimator>
{
private static readonly CornerRadiusAnimator s_animator = new CornerRadiusAnimator();
/// <inheritdocs/>
public override IObservable<CornerRadius> DoTransition(IObservable<double> progress, CornerRadius oldValue, CornerRadius newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

13
src/Avalonia.Visuals/Animation/Transitions/PointTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -8,15 +5,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Point"/> type.
/// </summary>
public class PointTransition : Transition<Point>
public class PointTransition : AnimatorDrivenTransition<Point, PointAnimator>
{
private static readonly PointAnimator s_animator = new PointAnimator();
/// <inheritdocs/>
public override IObservable<Point> DoTransition(IObservable<double> progress, Point oldValue, Point newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

13
src/Avalonia.Visuals/Animation/Transitions/SizeTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -8,15 +5,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Size"/> type.
/// </summary>
public class SizeTransition : Transition<Size>
public class SizeTransition : AnimatorDrivenTransition<Size, SizeAnimator>
{
private static readonly SizeAnimator s_animator = new SizeAnimator();
/// <inheritdocs/>
public override IObservable<Size> DoTransition(IObservable<double> progress, Size oldValue, Size newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

13
src/Avalonia.Visuals/Animation/Transitions/ThicknessTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -8,15 +5,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Thickness"/> type.
/// </summary>
public class ThicknessTransition : Transition<Thickness>
public class ThicknessTransition : AnimatorDrivenTransition<Thickness, ThicknessAnimator>
{
private static readonly ThicknessAnimator s_animator = new ThicknessAnimator();
/// <inheritdocs/>
public override IObservable<Thickness> DoTransition(IObservable<double> progress, Thickness oldValue, Thickness newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

13
src/Avalonia.Visuals/Animation/Transitions/VectorTransition.cs

@ -1,6 +1,3 @@
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Animators;
namespace Avalonia.Animation
@ -8,15 +5,7 @@ namespace Avalonia.Animation
/// <summary>
/// Transition class that handles <see cref="AvaloniaProperty"/> with <see cref="Vector"/> type.
/// </summary>
public class VectorTransition : Transition<Vector>
public class VectorTransition : AnimatorDrivenTransition<Vector, VectorAnimator>
{
private static readonly VectorAnimator s_animator = new VectorAnimator();
/// <inheritdocs/>
public override IObservable<Vector> DoTransition(IObservable<double> progress, Vector oldValue, Vector newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
}

37
tests/Avalonia.Benchmarks/Animations/TransitionBenchmark.cs

@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Runtime.CompilerServices;
using Avalonia.Animation;
using Avalonia.Animation.Animators;
using Avalonia.Layout;
using BenchmarkDotNet.Attributes;
@ -14,26 +16,29 @@ namespace Avalonia.Benchmarks.Animations
{
private readonly DoubleTransition _transition;
private readonly DoubleTransitionOld _oldTransition;
private readonly int _frameCount;
private readonly Subject<double> _timeProducer;
private readonly List<double> _producedValues;
private readonly AddValueObserver _observer;
[Params(10, 100)]
public int FrameCount { get; set; }
public TransitionBenchmark()
{
_frameCount = 100;
_oldTransition = new DoubleTransitionOld
{
Duration = TimeSpan.FromMilliseconds(_frameCount), Property = Layoutable.WidthProperty
Duration = TimeSpan.FromMilliseconds(FrameCount), Property = Layoutable.WidthProperty
};
_transition = new DoubleTransition
{
Duration = TimeSpan.FromMilliseconds(_frameCount), Property = Layoutable.WidthProperty
Duration = TimeSpan.FromMilliseconds(FrameCount), Property = Layoutable.WidthProperty
};
_timeProducer = new Subject<double>();
_producedValues = new List<double>(_frameCount);
_producedValues = new List<double>(FrameCount);
_observer = new AddValueObserver(_producedValues);
}
[Benchmark(Baseline = true)]
@ -56,14 +61,26 @@ namespace Avalonia.Benchmarks.Animations
_producedValues.Clear();
using var transitionSub = transitionObs.Subscribe(new AddValueObserver(_producedValues));
using var transitionSub = transitionObs.Subscribe(_observer);
for (int i = 0; i < _frameCount; i++)
for (int i = 0; i < FrameCount; i++)
{
_timeProducer.OnNext(TimeSpan.FromMilliseconds(i).TotalSeconds);
_timeProducer.OnNext(i/1000d);
}
Debug.Assert(_producedValues.Count == _frameCount);
Debug.Assert(_producedValues.Count == FrameCount);
}
private class DoubleTransitionOld : Transition<double>
{
private static readonly DoubleAnimator s_animator = new DoubleAnimator();
/// <inheritdocs/>
public override IObservable<double> DoTransition(IObservable<double> progress, double oldValue, double newValue)
{
return progress
.Select(progress => s_animator.Interpolate(Easing.Ease(progress), oldValue, newValue));
}
}
private class AddValueObserver : IObserver<double>

Loading…
Cancel
Save