committed by
GitHub
22 changed files with 279 additions and 150 deletions
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
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(); |
||||
|
|
||||
|
public override IObservable<T> DoTransition(IObservable<double> progress, T oldValue, T newValue) |
||||
|
{ |
||||
|
return new AnimatorTransitionObservable<T, TAnimator>(s_animator, progress, Easing, oldValue, newValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Animation.Animators; |
||||
|
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; |
||||
|
private readonly Easing _easing; |
||||
|
private readonly T _oldValue; |
||||
|
private readonly T _newValue; |
||||
|
|
||||
|
public AnimatorTransitionObservable(TAnimator animator, IObservable<double> progress, Easing easing, T oldValue, T newValue) : base(progress, easing) |
||||
|
{ |
||||
|
_animator = animator; |
||||
|
_easing = easing; |
||||
|
_oldValue = oldValue; |
||||
|
_newValue = newValue; |
||||
|
} |
||||
|
|
||||
|
protected override T ProduceValue(double progress) |
||||
|
{ |
||||
|
return _animator.Interpolate(progress, _oldValue, _newValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +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, Easing easing) |
||||
|
{ |
||||
|
_progress = progress; |
||||
|
_easing = easing; |
||||
|
} |
||||
|
|
||||
|
/// <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() |
||||
|
{ |
||||
|
_progressSubscription = _progress.Subscribe(this); |
||||
|
} |
||||
|
|
||||
|
protected override void Unsubscribed() |
||||
|
{ |
||||
|
_progressSubscription?.Dispose(); |
||||
|
} |
||||
|
|
||||
|
void IObserver<double>.OnCompleted() |
||||
|
{ |
||||
|
PublishCompleted(); |
||||
|
} |
||||
|
|
||||
|
void IObserver<double>.OnError(Exception error) |
||||
|
{ |
||||
|
PublishError(error); |
||||
|
} |
||||
|
|
||||
|
void IObserver<double>.OnNext(double value) |
||||
|
{ |
||||
|
double progress = _easing.Ease(value); |
||||
|
|
||||
|
PublishNext(ProduceValue(progress)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -1,28 +1,26 @@ |
|||||
using System; |
using System; |
||||
using System.Reactive.Linq; |
|
||||
using Avalonia.Animation.Animators; |
using Avalonia.Animation.Animators; |
||||
using Avalonia.Media; |
using Avalonia.Media; |
||||
|
using Avalonia.Media.Transformation; |
||||
|
|
||||
|
#nullable enable |
||||
|
|
||||
namespace Avalonia.Animation |
namespace Avalonia.Animation |
||||
{ |
{ |
||||
public class TransformOperationsTransition : Transition<ITransform> |
public class TransformOperationsTransition : Transition<ITransform> |
||||
{ |
{ |
||||
private static readonly TransformOperationsAnimator _operationsAnimator = new TransformOperationsAnimator(); |
private static readonly TransformOperationsAnimator s_operationsAnimator = new TransformOperationsAnimator(); |
||||
|
|
||||
public override IObservable<ITransform> DoTransition(IObservable<double> progress, |
public override IObservable<ITransform> DoTransition( |
||||
|
IObservable<double> progress, |
||||
ITransform oldValue, |
ITransform oldValue, |
||||
ITransform newValue) |
ITransform newValue) |
||||
{ |
{ |
||||
var oldTransform = TransformOperationsAnimator.EnsureOperations(oldValue); |
var oldTransform = TransformOperationsAnimator.EnsureOperations(oldValue); |
||||
var newTransform = TransformOperationsAnimator.EnsureOperations(newValue); |
var newTransform = TransformOperationsAnimator.EnsureOperations(newValue); |
||||
|
|
||||
return progress |
return new AnimatorTransitionObservable<TransformOperations, TransformOperationsAnimator>( |
||||
.Select(p => |
s_operationsAnimator, progress, Easing, oldTransform, newTransform); |
||||
{ |
|
||||
var f = Easing.Ease(p); |
|
||||
|
|
||||
return _operationsAnimator.Interpolate(f, oldTransform, newTransform); |
|
||||
}); |
|
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,78 @@ |
|||||
|
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; |
||||
|
|
||||
|
namespace Avalonia.Benchmarks.Animations |
||||
|
{ |
||||
|
[MemoryDiagnoser] |
||||
|
public class TransitionBenchmark |
||||
|
{ |
||||
|
private readonly AddValueObserver _observer; |
||||
|
private readonly List<double> _producedValues; |
||||
|
private readonly Subject<double> _timeProducer; |
||||
|
private readonly DoubleTransition _transition; |
||||
|
|
||||
|
public TransitionBenchmark() |
||||
|
{ |
||||
|
_transition = new DoubleTransition |
||||
|
{ |
||||
|
Duration = TimeSpan.FromMilliseconds(FrameCount), Property = Layoutable.WidthProperty |
||||
|
}; |
||||
|
|
||||
|
_timeProducer = new Subject<double>(); |
||||
|
_producedValues = new List<double>(FrameCount); |
||||
|
|
||||
|
_observer = new AddValueObserver(_producedValues); |
||||
|
} |
||||
|
|
||||
|
[Params(10, 100)] public int FrameCount { get; set; } |
||||
|
|
||||
|
[Benchmark] |
||||
|
[MethodImpl(MethodImplOptions.NoInlining)] |
||||
|
public void NewTransition() |
||||
|
{ |
||||
|
var transitionObs = _transition.DoTransition(_timeProducer, 0, 1); |
||||
|
|
||||
|
_producedValues.Clear(); |
||||
|
|
||||
|
using var transitionSub = transitionObs.Subscribe(_observer); |
||||
|
|
||||
|
for (int i = 0; i < FrameCount; i++) |
||||
|
{ |
||||
|
_timeProducer.OnNext(i / 1000d); |
||||
|
} |
||||
|
|
||||
|
Debug.Assert(_producedValues.Count == FrameCount); |
||||
|
} |
||||
|
|
||||
|
private class AddValueObserver : IObserver<double> |
||||
|
{ |
||||
|
private readonly List<double> _values; |
||||
|
|
||||
|
public AddValueObserver(List<double> values) |
||||
|
{ |
||||
|
_values = values; |
||||
|
} |
||||
|
|
||||
|
public void OnCompleted() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void OnError(Exception error) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void OnNext(double value) |
||||
|
{ |
||||
|
_values.Add(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue