diff --git a/Cairo/Perspex.Cairo/Perspex.Cairo.csproj b/Cairo/Perspex.Cairo/Perspex.Cairo.csproj
index 12985d10b8..c8843d6523 100644
--- a/Cairo/Perspex.Cairo/Perspex.Cairo.csproj
+++ b/Cairo/Perspex.Cairo/Perspex.Cairo.csproj
@@ -76,6 +76,10 @@
+
+ {d211e587-d8bc-45b9-95a4-f297c8fa5200}
+ Perspex.Animation
+
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}
Perspex.Base
diff --git a/Perspex.Animation/Animate.cs b/Perspex.Animation/Animate.cs
index ac08c6a2ab..266585a13f 100644
--- a/Perspex.Animation/Animate.cs
+++ b/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;
+ ///
+ /// Utilities for creating animations.
+ ///
public static class Animate
{
- private const int FramesPerSecond = 30;
+ ///
+ /// The number of frames per second.
+ ///
+ public const int FramesPerSecond = 60;
+ ///
+ /// The time span of each frame.
+ ///
private static readonly TimeSpan Tick = TimeSpan.FromSeconds(1.0 / FramesPerSecond);
+ ///
+ /// Initializes the static class.
+ ///
+ static Animate()
+ {
+ Stopwatch = new Stopwatch();
+ Stopwatch.Start();
+ Timer = Observable.Interval(Tick, PerspexScheduler.Instance).Select(_ => Stopwatch.Elapsed);
+ }
+
+ ///
+ /// The stopwatch used to track time.
+ ///
+ public static Stopwatch Stopwatch
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets the animation timer.
+ ///
+ ///
+ /// The animation timer ticks times per second. The
+ /// parameter passed to a subsciber is the time span since the animation system was
+ /// initialized.
+ ///
+ public static IObservable Timer
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Gets a timer that fires every frame for the specified duration.
+ ///
+ /// The duration of the animation.
+ ///
+ /// An observable that notifies the subscriber of the progress along the animation.
+ ///
+ ///
+ /// 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.
+ ///
+ public static IObservable 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));
+ }
+
+ ///
+ /// Animates a .
+ ///
+ /// The property type.
+ /// The target object.
+ /// The target property.
+ /// The value of the property at the start of the animation.
+ /// The value of the property at the end of the animation.
+ /// The easing function to use.
+ /// The duration of the animation.
+ /// An that can be used to stop the animation.
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);
}
+ ///
+ /// Animates a .
+ ///
+ /// The property type.
+ /// The target object.
+ /// The target property.
+ /// The value of the property at the start of the animation.
+ /// The value of the property at the end of the animation.
+ /// The easing function to use.
+ /// The duration of the animation.
+ /// An that can be used to stop the animation.
public static IDisposable Property(
PerspexObject target,
PerspexProperty property,
T start,
T finish,
- IEasing easing,
+ IEasing 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);
}
}
}
diff --git a/Perspex.SceneGraph/IVisual.cs b/Perspex.SceneGraph/IVisual.cs
index 1f1aa8775a..12a1104864 100644
--- a/Perspex.SceneGraph/IVisual.cs
+++ b/Perspex.SceneGraph/IVisual.cs
@@ -46,7 +46,7 @@ namespace Perspex
///
/// Gets the render transform of the scene graph node.
///
- ITransform RenderTransform { get; }
+ Transform RenderTransform { get; }
///
/// Gets the transform origin of the scene graph node.
diff --git a/Perspex.SceneGraph/Media/Geometry.cs b/Perspex.SceneGraph/Media/Geometry.cs
index b0c971576f..0d30a8ca3b 100644
--- a/Perspex.SceneGraph/Media/Geometry.cs
+++ b/Perspex.SceneGraph/Media/Geometry.cs
@@ -11,14 +11,14 @@ namespace Perspex.Media
public abstract class Geometry : PerspexObject
{
- public static readonly PerspexProperty TransformProperty =
- PerspexProperty.Register("Transform");
+ public static readonly PerspexProperty TransformProperty =
+ PerspexProperty.Register("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); }
diff --git a/Perspex.SceneGraph/Media/ITransform.cs b/Perspex.SceneGraph/Media/ITransform.cs
deleted file mode 100644
index 248063d6ec..0000000000
--- a/Perspex.SceneGraph/Media/ITransform.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// -----------------------------------------------------------------------
-//
-// Copyright 2014 MIT Licence. See licence.md for more information.
-//
-// -----------------------------------------------------------------------
-
-namespace Perspex.Media
-{
- using System;
-
- public interface ITransform
- {
- event EventHandler Changed;
-
- Matrix Value { get; }
- }
-}
diff --git a/Perspex.SceneGraph/Media/Transform.cs b/Perspex.SceneGraph/Media/Transform.cs
index fa8fc6abe6..af6cb16832 100644
--- a/Perspex.SceneGraph/Media/Transform.cs
+++ b/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;
diff --git a/Perspex.SceneGraph/Perspex.SceneGraph.csproj b/Perspex.SceneGraph/Perspex.SceneGraph.csproj
index 81cba21aaa..c3ee00e99c 100644
--- a/Perspex.SceneGraph/Perspex.SceneGraph.csproj
+++ b/Perspex.SceneGraph/Perspex.SceneGraph.csproj
@@ -59,7 +59,6 @@
-
diff --git a/Perspex.SceneGraph/Visual.cs b/Perspex.SceneGraph/Visual.cs
index 3edb0d72f1..28eed3521f 100644
--- a/Perspex.SceneGraph/Visual.cs
+++ b/Perspex.SceneGraph/Visual.cs
@@ -27,8 +27,8 @@ namespace Perspex
public static readonly PerspexProperty OpacityProperty =
PerspexProperty.Register("Opacity", 1);
- public static readonly PerspexProperty RenderTransformProperty =
- PerspexProperty.Register("RenderTransform");
+ public static readonly PerspexProperty RenderTransformProperty =
+ PerspexProperty.Register("RenderTransform");
public static readonly PerspexProperty TransformOriginProperty =
PerspexProperty.Register("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)
{
diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs
index 436aa0bdfa..eacf07597c 100644
--- a/TestApplication/Program.cs
+++ b/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;
}
diff --git a/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj b/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
index 2cb46de7b2..f7fee0a1d5 100644
--- a/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
+++ b/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
@@ -85,6 +85,10 @@
+
+ {d211e587-d8bc-45b9-95a4-f297c8fa5200}
+ Perspex.Animation
+
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}
Perspex.Base