5
// Jason Smith <jason.smith@xamarin.com>
7
// Copyright (c) 2012 Xamarin Inc.
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28
using System.Collections.Generic;
29
using System.Diagnostics;
31
namespace MonoDevelop.Components
34
public static class Easing
36
public static readonly Func<float, float> Linear = x => x;
38
public static readonly Func<float, float> SinOut = x => (float)Math.Sin (x * Math.PI * 0.5f);
39
public static readonly Func<float, float> SinIn = x => 1.0f - (float)Math.Cos (x * Math.PI * 0.5f);
40
public static readonly Func<float, float> SinInOut = x => -(float)Math.Cos (Math.PI * x) / 2.0f + 0.5f;
42
public static readonly Func<float, float> CubicIn = x => x * x * x;
43
public static readonly Func<float, float> CubicOut = x => (float)Math.Pow (x - 1.0f, 3.0f) + 1.0f;
44
public static readonly Func<float, float> CubicInOut = x => x < 0.5f ? (float)Math.Pow (x * 2.0f, 3.0f) / 2.0f :
45
(float)(Math.Pow ((x-1)*2.0f, 3.0f) + 2.0f) / 2.0f;
47
public static readonly Func<float, float> BounceOut;
48
public static readonly Func<float, float> BounceIn;
50
public static readonly Func<float, float> SpringIn = x => x * x * ((1.70158f + 1) * x - 1.70158f);
51
public static readonly Func<float, float> SpringOut = x => (x - 1) * (x - 1) * ((1.70158f + 1) * (x - 1) + 1.70158f) + 1;
58
return 7.5625f * p * p;
60
else if (p < (2 / 2.75f))
64
return 7.5625f * p * p + .75f;
66
else if (p < (2.5f / 2.75f))
70
return 7.5625f * p * p + .9375f;
74
p -= (2.625f / 2.75f);
76
return 7.5625f * p * p + .984375f;
80
BounceIn = p => 1.0f - BounceOut (p);
84
public interface Animatable
89
public class Animation : System.Collections.IEnumerable
93
Func<float, float> easing;
95
List<Animation> children;
97
bool finishedTriggered;
101
children = new List<Animation> ();
102
easing = Easing.Linear;
106
public Animation (Action<float> callback, float start = 0.0f, float end = 1.0f, Func<float, float> easing = null, Action finished = null)
108
children = new List<Animation> ();
109
this.easing = easing ?? Components.Easing.Linear;
110
this.finished = finished;
112
var transform = AnimationExtensions.Interpolate (start, end);
113
step = f => callback (transform (f));
116
public System.Collections.IEnumerator GetEnumerator ()
118
return children.GetEnumerator ();
121
public Animation Insert (float beginAt, float finishAt, Animation animation)
123
Add (beginAt, finishAt, animation);
127
public void Commit (Animatable owner, string name, uint rate = 16, uint length = 250,
128
Func<float, float> easing = null, Action<float, bool> finished = null, Func<bool> repeat = null)
130
owner.Animate (name, this, rate, length, easing, finished, repeat);
133
public void Add (float beginAt, float finishAt, Animation animation)
135
if (beginAt < 0 || beginAt > 1)
136
throw new ArgumentOutOfRangeException ("beginAt");
138
if (finishAt < 0 || finishAt > 1)
139
throw new ArgumentOutOfRangeException ("finishAt");
141
if (finishAt <= beginAt)
142
throw new ArgumentException ("finishAt must be greater than beginAt");
144
animation.beginAt = beginAt;
145
animation.finishAt = finishAt;
146
children.Add (animation);
149
public Animation WithConcurrent (Animation animation, float beginAt = 0.0f, float finishAt = 1.0f)
151
animation.beginAt = beginAt;
152
animation.finishAt = finishAt;
153
children.Add (animation);
157
public Animation WithConcurrent (Action<float> callback, float start = 0.0f, float end = 1.0f, Func<float, float> easing = null, float beginAt = 0.0f, float finishAt = 1.0f)
159
Animation child = new Animation (callback, start, end, easing);
160
child.beginAt = beginAt;
161
child.finishAt = finishAt;
162
children.Add (child);
166
public Action<float> GetCallback ()
168
Action<float> result = f => {
170
foreach (var animation in children) {
171
if (animation.finishedTriggered)
174
float val = Math.Max (0.0f, Math.Min (1.0f, (f - animation.beginAt) / (animation.finishAt - animation.beginAt)));
176
if (val <= 0.0f) // not ready to process yet
179
var callback = animation.GetCallback ();
183
animation.finishedTriggered = true;
184
if (animation.finished != null)
185
animation.finished ();
193
public static class AnimationExtensions
197
public Func<float, float> Easing { get; set; }
198
public uint Rate { get; set; }
199
public uint Length { get; set; }
200
public Animatable Owner { get; set; }
201
public Action<float> callback;
202
public Action<float, bool> finished;
203
public Func<bool> repeat;
204
public Tweener tweener;
207
static Dictionary<string, Info> animations;
209
static AnimationExtensions ()
211
animations = new Dictionary<string, Info> ();
214
public static void Animate (this Animatable self, string name, Animation animation, uint rate = 16, uint length = 250,
215
Func<float, float> easing = null, Action<float, bool> finished = null, Func<bool> repeat = null)
217
self.Animate (name, animation.GetCallback (), rate, length, easing, finished, repeat);
220
public static Func<float, float> Interpolate (float start, float end = 1.0f, float reverseVal = 0.0f, bool reverse = false)
222
float target = (reverse ? reverseVal : end);
223
return x => start + (target - start) * x;
226
public static void Animate (this Animatable self, string name, Action<float> callback, float start, float end, uint rate = 16, uint length = 250,
227
Func<float, float> easing = null, Action<float, bool> finished = null, Func<bool> repeat = null)
229
self.Animate<float> (name, Interpolate (start, end), callback, rate, length, easing, finished, repeat);
232
public static void Animate (this Animatable self, string name, Action<float> callback, uint rate = 16, uint length = 250,
233
Func<float, float> easing = null, Action<float, bool> finished = null, Func<bool> repeat = null)
235
self.Animate<float> (name, x => x, callback, rate, length, easing, finished, repeat);
238
public static void Animate<T> (this Animatable self, string name, Func<float, T> transform, Action<T> callback, uint rate = 16, uint length = 250,
239
Func<float, float> easing = null, Action<T, bool> finished = null, Func<bool> repeat = null)
241
if (transform == null)
242
throw new ArgumentNullException ("transform");
243
if (callback == null)
244
throw new ArgumentNullException ("callback");
246
throw new ArgumentNullException ("widget");
248
self.AbortAnimation (name);
249
name += self.GetHashCode ().ToString ();
251
Action<float> step = f => callback (transform(f));
252
Action<float, bool> final = null;
253
if (finished != null)
254
final = (f, b) => finished (transform(f), b);
256
var info = new Info {
259
Easing = easing ?? Easing.Linear
262
Tweener tweener = new Tweener (info.Length, info.Rate);
263
tweener.Easing = info.Easing;
264
tweener.Handle = name;
265
tweener.ValueUpdated += HandleTweenerUpdated;
266
tweener.Finished += HandleTweenerFinished;
268
info.tweener = tweener;
269
info.callback = step;
270
info.finished = final;
271
info.repeat = repeat ?? (() => false);
274
animations[name] = info;
277
info.callback (0.0f);
280
public static bool AbortAnimation (this Animatable self, string handle)
282
handle += self.GetHashCode ().ToString ();
283
if (!animations.ContainsKey (handle))
286
Info info = animations[handle];
287
info.tweener.ValueUpdated -= HandleTweenerUpdated;
288
info.tweener.Finished -= HandleTweenerFinished;
289
info.tweener.Stop ();
291
animations.Remove (handle);
292
if (info.finished != null)
293
info.finished (1.0f, true);
297
public static bool AnimationIsRunning (this Animatable self, string handle)
299
handle += self.GetHashCode ().ToString ();
300
return animations.ContainsKey (handle);
303
static void HandleTweenerUpdated (object o, EventArgs args)
305
Tweener tweener = o as Tweener;
306
Info info = animations[tweener.Handle];
308
info.callback (tweener.Value);
309
info.Owner.QueueDraw ();
312
static void HandleTweenerFinished (object o, EventArgs args)
314
Tweener tweener = o as Tweener;
315
Info info = animations[tweener.Handle];
317
bool repeat = info.repeat ();
319
info.callback (tweener.Value);
322
animations.Remove (tweener.Handle);
323
tweener.ValueUpdated -= HandleTweenerUpdated;
324
tweener.Finished -= HandleTweenerFinished;
327
if (info.finished != null)
328
info.finished (tweener.Value, false);
329
info.Owner.QueueDraw ();
339
public uint Length { get; private set; }
340
public uint Rate { get; private set; }
341
public float Value { get; private set; }
342
public Func<float, float> Easing { get; set; }
343
public bool Loop { get; set; }
344
public string Handle { get; set; }
346
public bool IsRunning {
347
get { return runningTime.IsRunning; }
350
public event EventHandler ValueUpdated;
351
public event EventHandler Finished;
353
Stopwatch runningTime;
356
public Tweener (uint length, uint rate)
362
runningTime = new Stopwatch ();
363
Easing = Components.Easing.Linear;
368
if (timeoutHandle > 0)
369
GLib.Source.Remove (timeoutHandle);
376
runningTime.Start ();
377
timeoutHandle = GLib.Timeout.Add (Rate, () => {
378
float rawValue = Math.Min (1.0f, runningTime.ElapsedMilliseconds / (float) Length);
379
Value = Easing (rawValue);
380
if (ValueUpdated != null)
381
ValueUpdated (this, EventArgs.Empty);
383
if (rawValue >= 1.0f)
387
runningTime.Reset ();
388
runningTime.Start ();
393
runningTime.Reset ();
395
if (Finished != null)
396
Finished (this, EventArgs.Empty);
407
runningTime.Reset ();
409
if (Finished != null)
410
Finished (this, EventArgs.Empty);
416
runningTime.Reset ();
417
runningTime.Start ();
424
if (timeoutHandle > 0) {
425
GLib.Source.Remove (timeoutHandle);