Browse Source

Implement a new timing engine for Transitions. Transtions are fixed now.Move out some stuff from Timing.cs.

pull/1793/head
Jumar Macato 8 years ago
parent
commit
3d7516cc0c
No known key found for this signature in database GPG Key ID: B19884DAC3A5BF3F
  1. 6
      samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs
  2. 5
      src/Avalonia.Animation/Animation.cs
  3. 4
      src/Avalonia.Animation/AnimationsEngine`1.cs
  4. 2
      src/Avalonia.Animation/Animator`1.cs
  5. 38
      src/Avalonia.Animation/Timing.cs
  6. 5
      src/Avalonia.Animation/Transition`1.cs
  7. 58
      src/Avalonia.Animation/TransitionsEngine.cs

6
samples/RenderDemo/ViewModels/AnimationsPageViewModel.cs

@ -15,16 +15,16 @@ namespace RenderDemo.ViewModels
void TogglePlayState()
{
switch (Timing.GlobalPlayState)
switch (Animation.GlobalPlayState)
{
case PlayState.Run:
PlayStateText = "Resume all animations";
Timing.GlobalPlayState = PlayState.Pause;
Animation.GlobalPlayState = PlayState.Pause;
break;
case PlayState.Pause:
PlayStateText = "Pause all animations";
Timing.GlobalPlayState = PlayState.Run;
Animation.GlobalPlayState = PlayState.Run;
break;
}
}

5
src/Avalonia.Animation/Animation.cs

@ -21,6 +21,10 @@ namespace Avalonia.Animation
/// </summary>
public class Animation : AvaloniaList<KeyFrame>, IAnimation
{
/// <summary>
/// Gets or sets the animation play state for all animations
/// </summary>
public static PlayState GlobalPlayState { get; set; } = PlayState.Run;
public AvaloniaList<IAnimator> _animators { get; set; } = new AvaloniaList<IAnimator>();
@ -73,7 +77,6 @@ namespace Avalonia.Animation
/// </remarks>
public bool DelayBetweenIterations { get; set; }
private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)>
{
( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) )

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

@ -111,7 +111,7 @@ namespace Avalonia.Animation
private void DoPlayStatesAndTime(TimeSpan systemTime)
{
if (Timing.GlobalPlayState == PlayState.Stop || targetControl.PlayState == PlayState.Stop)
if (Animation.GlobalPlayState == PlayState.Stop || targetControl.PlayState == PlayState.Stop)
DoComplete();
if (!previousClock.HasValue)
@ -121,7 +121,7 @@ namespace Avalonia.Animation
}
else
{
if (Timing.GlobalPlayState == PlayState.Pause || targetControl.PlayState == PlayState.Pause)
if (Animation.GlobalPlayState == PlayState.Pause || targetControl.PlayState == PlayState.Pause)
{
previousClock = systemTime;
return;

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

@ -98,7 +98,7 @@ namespace Avalonia.Animation
/// </summary>
private IDisposable RunKeyFrames(Animation animation, Animatable control, Action onComplete)
{
var stateMachine = new AnimatorStateMachine<T>(animation, control, this, onComplete);
var stateMachine = new AnimationsEngine<T>(animation, control, this, onComplete);
Timing.AnimationsTimer
.TakeWhile(_ => !stateMachine.unsubscribe)

38
src/Avalonia.Animation/Timing.cs

@ -15,11 +15,6 @@ namespace Avalonia.Animation
/// </summary>
public static class Timing
{
/// <summary>
/// Gets or sets the animation play state for all animations
/// </summary>
public static PlayState GlobalPlayState { get; set; } = PlayState.Run;
/// <summary>
/// The number of frames per second.
/// </summary>
@ -38,14 +33,13 @@ namespace Avalonia.Animation
var globalTimer = Observable.Interval(FrameTick, AvaloniaScheduler.Instance);
AnimationsTimer = globalTimer
.Select(_ =>
{
return TimeSpan.FromMilliseconds(Environment.TickCount);
})
.Select(_ => GetTickCount())
.Publish()
.RefCount();
}
internal static TimeSpan GetTickCount() => TimeSpan.FromMilliseconds(Environment.TickCount);
/// <summary>
/// Gets the animation timer.
/// </summary>
@ -58,31 +52,5 @@ namespace Avalonia.Animation
{
get;
}
/// <summary>
/// Gets a timer that fires every frame for the specified duration with delay.
/// </summary>
/// <returns>
/// An observable that notifies the subscriber of the progress along the transition.
/// </returns>
/// <remarks>
/// The parameter passed to the subscriber is the progress along the transition, with
/// 0 being the start and 1 being the end. The observable is guaranteed to fire 0
/// immediately on subscribe and 1 at the end of the duration.
/// </remarks>
public static IObservable<double> GetTransitionsTimer(Animatable control, TimeSpan duration, TimeSpan delay = default(TimeSpan))
{
// TODO: Fix this mess.
// var _duration = (duration.Ticks / FrameTick.Ticks);
// long? endTime = ((Stopwatch.GetTimestamp() - _tickStartTimeStamp)
// / TicksPerFrame) + _duration;
// return AnimationsTimer
// .TakeWhile(x => x < endTime)
// .Select(x => (double)x / _duration)
// .StartWith(0.0)
// .Concat(Observable.Return(1.0));
return Observable.Empty<double>();
}
}
}

5
src/Avalonia.Animation/Transition`1.cs

@ -5,6 +5,7 @@ using Avalonia.Metadata;
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Easings;
using Avalonia.Animation.Utils;
namespace Avalonia.Animation
{
@ -51,8 +52,10 @@ namespace Avalonia.Animation
/// <inheritdocs/>
public virtual IDisposable Apply(Animatable control, object oldValue, object newValue)
{
var transition = DoTransition(Timing.GetTransitionsTimer(control, Duration, TimeSpan.Zero), (T)oldValue, (T)newValue);
var transition = DoTransition(new TransitionsEngine(Duration), (T)oldValue, (T)newValue);
return control.Bind<T>((AvaloniaProperty<T>)Property, transition, Data.BindingPriority.Animation);
}
}
}

58
src/Avalonia.Animation/TransitionsEngine.cs

@ -0,0 +1,58 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Metadata;
using System;
using System.Reactive.Linq;
using Avalonia.Animation.Easings;
using Avalonia.Animation.Utils;
namespace Avalonia.Animation
{
public class TransitionsEngine : IObservable<double>, IDisposable
{
private IObserver<double> observer;
private IDisposable timerSubscription;
private readonly TimeSpan startTime;
private readonly TimeSpan duration;
public TransitionsEngine(TimeSpan Duration)
{
startTime = Timing.GetTickCount();
duration = Duration;
timerSubscription = Timing
.AnimationsTimer
.Subscribe(t => TimerTick(t));
}
private void TimerTick(TimeSpan t)
{
var interpVal = (double)(t.Ticks - startTime.Ticks) / duration.Ticks;
if (interpVal > 1d
|| interpVal < 0d)
{
this.Dispose();
return;
}
observer?.OnNext(interpVal);
}
public void Dispose()
{
timerSubscription?.Dispose();
observer?.OnCompleted();
}
public IDisposable Subscribe(IObserver<double> Observer)
{
if (Observer is null)
throw new InvalidProgramException("Can only set the subscription once.");
observer = Observer;
return this;
}
}
}
Loading…
Cancel
Save