Browse Source

Use a single timer for all animation.

Still renders >60fps when there are two concurrent animations though.
Hmm.
pull/16/head
Steven Kirk 12 years ago
parent
commit
5e8bb904c9
  1. 4
      Cairo/Perspex.Cairo/Perspex.Cairo.csproj
  2. 110
      Perspex.Animation/Animate.cs
  3. 2
      Perspex.SceneGraph/IVisual.cs
  4. 8
      Perspex.SceneGraph/Media/Geometry.cs
  5. 17
      Perspex.SceneGraph/Media/ITransform.cs
  6. 2
      Perspex.SceneGraph/Media/Transform.cs
  7. 1
      Perspex.SceneGraph/Perspex.SceneGraph.csproj
  8. 10
      Perspex.SceneGraph/Visual.cs
  9. 20
      TestApplication/Program.cs
  10. 4
      Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj

4
Cairo/Perspex.Cairo/Perspex.Cairo.csproj

@ -76,6 +76,10 @@
<Compile Include="Media\StreamGeometryImpl.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Perspex.Animation\Perspex.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Perspex.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Base\Perspex.Base.csproj">
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Perspex.Base</Name>

110
Perspex.Animation/Animate.cs

@ -7,48 +7,126 @@
namespace Perspex.Animation
{
using System;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using Perspex.Threading;
/// <summary>
/// Utilities for creating animations.
/// </summary>
public static class Animate
{
private const int FramesPerSecond = 30;
/// <summary>
/// The number of frames per second.
/// </summary>
public const int FramesPerSecond = 60;
/// <summary>
/// The time span of each frame.
/// </summary>
private static readonly TimeSpan Tick = TimeSpan.FromSeconds(1.0 / FramesPerSecond);
/// <summary>
/// Initializes the static class.
/// </summary>
static Animate()
{
Stopwatch = new Stopwatch();
Stopwatch.Start();
Timer = Observable.Interval(Tick, PerspexScheduler.Instance).Select(_ => Stopwatch.Elapsed);
}
/// <summary>
/// The stopwatch used to track time.
/// </summary>
public static Stopwatch Stopwatch
{
get;
private set;
}
/// <summary>
/// Gets the animation timer.
/// </summary>
/// <remarks>
/// The animation timer ticks <see cref="FramesPerSecond"/> times per second. The
/// parameter passed to a subsciber is the time span since the animation system was
/// initialized.
/// </remarks>
public static IObservable<TimeSpan> Timer
{
get;
private set;
}
/// <summary>
/// Gets a timer that fires every frame for the specified duration.
/// </summary>
/// <param name="duration">The duration of the animation.</param>
/// <returns>
/// An observable that notifies the subscriber of the progress along the animation.
/// </returns>
/// <remarks>
/// The parameter passed to the subscriber is the progress along the animation, 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> GetTimer(TimeSpan duration)
{
var startTime = Stopwatch.Elapsed.Ticks;
var endTime = (startTime + duration.Ticks);
return Timer
.TakeWhile(x => x.Ticks < endTime)
.Select(x => (x.Ticks - startTime) / (double)duration.Ticks)
.StartWith(0.0)
.Concat(Observable.Return(1.0));
}
/// <summary>
/// Animates a <see cref="PerspexProperty"/>.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="target">The target object.</param>
/// <param name="property">The target property.</param>
/// <param name="start">The value of the property at the start of the animation.</param>
/// <param name="finish">The value of the property at the end of the animation.</param>
/// <param name="easing">The easing function to use.</param>
/// <param name="duration">The duration of the animation.</param>
/// <returns>An <see cref="IDisposable"/> that can be used to stop the animation.</returns>
public static IDisposable Property(
PerspexObject target,
PerspexProperty property,
object start,
object finish,
IEasing easing,
TimeSpan duration,
double repeats = 1)
TimeSpan duration)
{
var startTime = Environment.TickCount;
var runningTime = (double)duration.TotalMilliseconds;
var endTime = Environment.TickCount + (runningTime * repeats);
var o = Observable.Interval(Tick, PerspexScheduler.Instance)
.Select(_ => Environment.TickCount)
.TakeWhile(tick => tick < endTime)
.Select(tick => easing.Ease((tick - startTime) / runningTime, start, finish))
.StartWith(start)
.Concat(Observable.Return(finish));
var o = GetTimer(duration).Select(progress => easing.Ease(progress, start, finish));
return target.Bind(property, o, BindingPriority.Animation);
}
/// <summary>
/// Animates a <see cref="PerspexProperty"/>.
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="target">The target object.</param>
/// <param name="property">The target property.</param>
/// <param name="start">The value of the property at the start of the animation.</param>
/// <param name="finish">The value of the property at the end of the animation.</param>
/// <param name="easing">The easing function to use.</param>
/// <param name="duration">The duration of the animation.</param>
/// <returns>An <see cref="IDisposable"/> that can be used to stop the animation.</returns>
public static IDisposable Property<T>(
PerspexObject target,
PerspexProperty<T> property,
T start,
T finish,
IEasing easing,
IEasing<T> easing,
TimeSpan duration)
{
return Property(target, (PerspexProperty)property, start, finish, easing, duration);
var o = GetTimer(duration).Select(progress => easing.Ease(progress, start, finish));
return target.Bind(property, o, BindingPriority.Animation);
}
}
}

2
Perspex.SceneGraph/IVisual.cs

@ -46,7 +46,7 @@ namespace Perspex
/// <summary>
/// Gets the render transform of the scene graph node.
/// </summary>
ITransform RenderTransform { get; }
Transform RenderTransform { get; }
/// <summary>
/// Gets the transform origin of the scene graph node.

8
Perspex.SceneGraph/Media/Geometry.cs

@ -11,14 +11,14 @@ namespace Perspex.Media
public abstract class Geometry : PerspexObject
{
public static readonly PerspexProperty<ITransform> TransformProperty =
PerspexProperty.Register<Geometry, ITransform>("Transform");
public static readonly PerspexProperty<Transform> TransformProperty =
PerspexProperty.Register<Geometry, Transform>("Transform");
static Geometry()
{
TransformProperty.Changed.Subscribe(x =>
{
((Geometry)x.Sender).PlatformImpl.Transform = ((ITransform)x.NewValue).Value;
((Geometry)x.Sender).PlatformImpl.Transform = ((Transform)x.NewValue).Value;
});
}
@ -33,7 +33,7 @@ namespace Perspex.Media
protected set;
}
public ITransform Transform
public Transform Transform
{
get { return this.GetValue(TransformProperty); }
set { this.SetValue(TransformProperty, value); }

17
Perspex.SceneGraph/Media/ITransform.cs

@ -1,17 +0,0 @@
// -----------------------------------------------------------------------
// <copyright file="ITransform.cs" company="Steven Kirk">
// Copyright 2014 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Media
{
using System;
public interface ITransform
{
event EventHandler Changed;
Matrix Value { get; }
}
}

2
Perspex.SceneGraph/Media/Transform.cs

@ -9,7 +9,7 @@ namespace Perspex.Media
using System;
using Perspex.Animation;
public abstract class Transform : Animatable, ITransform
public abstract class Transform : Animatable
{
public event EventHandler Changed;

1
Perspex.SceneGraph/Perspex.SceneGraph.csproj

@ -59,7 +59,6 @@
<Compile Include="Media\Imaging\Bitmap.cs" />
<Compile Include="Media\Imaging\IBitmap.cs" />
<Compile Include="Media\Imaging\RenderTargetBitmap.cs" />
<Compile Include="Media\ITransform.cs" />
<Compile Include="Media\PathMarkupParser.cs" />
<Compile Include="Media\Pen.cs" />
<Compile Include="Media\EllipseGeometry.cs" />

10
Perspex.SceneGraph/Visual.cs

@ -27,8 +27,8 @@ namespace Perspex
public static readonly PerspexProperty<double> OpacityProperty =
PerspexProperty.Register<Visual, double>("Opacity", 1);
public static readonly PerspexProperty<ITransform> RenderTransformProperty =
PerspexProperty.Register<Visual, ITransform>("RenderTransform");
public static readonly PerspexProperty<Transform> RenderTransformProperty =
PerspexProperty.Register<Visual, Transform>("RenderTransform");
public static readonly PerspexProperty<Origin> TransformOriginProperty =
PerspexProperty.Register<Visual, Origin>("TransformOrigin", defaultValue: Origin.Default);
@ -69,7 +69,7 @@ namespace Perspex
set { this.SetValue(OpacityProperty, value); }
}
public ITransform RenderTransform
public Transform RenderTransform
{
get { return this.GetValue(RenderTransformProperty); }
set { this.SetValue(RenderTransformProperty, value); }
@ -215,8 +215,8 @@ namespace Perspex
if (sender != null)
{
var oldValue = e.OldValue as ITransform;
var newValue = e.NewValue as ITransform;
var oldValue = e.OldValue as Transform;
var newValue = e.NewValue as Transform;
if (oldValue != null)
{

20
TestApplication/Program.cs

@ -541,14 +541,20 @@ namespace TestApplication
}
};
Animate.Property(
(PerspexObject)rect1.RenderTransform,
var start = Animate.Stopwatch.Elapsed;
var degrees = Animate.Timer
.Select(x =>
{
var elapsed = (x - start).TotalSeconds;
var cycles = elapsed / 4;
var progress = cycles % 1;
return 360.0 * progress;
});
rect1.RenderTransform.Bind(
RotateTransform.AngleProperty,
0.0,
360.0,
new LinearDoubleEasing(),
TimeSpan.FromSeconds(4),
double.PositiveInfinity);
degrees,
BindingPriority.Animation);
return result;
}

4
Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj

@ -85,6 +85,10 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Perspex.Animation\Perspex.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Perspex.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\Perspex.Base\Perspex.Base.csproj">
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project>
<Name>Perspex.Base</Name>

Loading…
Cancel
Save