Skip to content

Instantly share code, notes, and snippets.

@davidortinau
Last active December 16, 2015 08:39
Show Gist options
  • Save davidortinau/5407892 to your computer and use it in GitHub Desktop.
Save davidortinau/5407892 to your computer and use it in GitHub Desktop.
Xamarin.iOS Example of using a TweenBuilder w/ EasingFunction delegate, and Easing equations courtesy of https://github.com/debreuil/Swf2XNA/blob/master/V2DRuntime/Tween/Easing.cs. What might make this more useful is to perhaps make the entire CAKeyframeAnimation in the builder (factory?) and return that instead of just the key values.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace V2DRuntime.Tween
{
public delegate float EasingFormula(float t, float start, float length);
// easing equations from the ever brilliant Robert Penner
public class Easing
{
public const float pi = (float)Math.PI;
public const float twoPi = (float)Math.PI * 2f;
public const float halfPi = (float)Math.PI / 2f;
// simple linear tweening - no easing
public static float Linear(float t, float start, float length)
{
return length * t + start;
}
///////////// QUADRATIC EASING: t^2 ///////////////////
// quadratic easing in - accelerating from zero velocity
public static float EaseInQuad(float t, float start, float length)
{
return length * t * t + start;
}
// quadratic easing out - decelerating to zero velocity
public static float EaseOutQuad(float t, float start, float length)
{
return -length * (t) * (t - 2) + start;
}
// quadratic easing in/out - acceleration until halfway, then deceleration
public static float EaseInOutQuad(float t, float start, float length)
{
if (t < .5f)
{
return length / 2f * t * t + start;
}
else
{
float t2 = 1f - t;
return length - length / 2f * t2 * t2 + start;
//return -length / 2f * ((--t) * (t - 2) - 1) + start;
}
}
public static float EaseInAndBackQuad(float t, float start, float length, params float[] center)
{
float c = center.Length > 0 ? center[0] : .5f;
if (t < c)
{
float t2 = t * (1f / c);
return length * t2 * t2 + start;
}
else
{
float t2 = (t - c) * (1f / (1f - c));
return length - length * t2 * t2 + start;
//return -length / 2f * ((--t) * (t - 2) - 1) + start;
}
}
public static float EaseOutAndBackQuad(float t, float start, float length, params float[] center)
{
float c = center.Length > 0 ? center[0] : .5f;
if (t < c)
{
float t2 = t * (1f / c);
return -length * t2 * (t2 - 2f) + start;
}
else
{
float rc = 1f - c;
float t2 = (t - c) * (1f / rc);
return length + length * t2 * (t2 - 1f - rc) + start;
//return -length / 2f * ((--t) * (t - 2) - 1) + start;
}
}
///////////// CUBIC EASING: t^3 ///////////////////////
// cubic easing in - accelerating from zero velocity
public static float EaseInCubic(float t, float start, float length)
{
return length * (t) * t * t + start;
}
// cubic easing out - decelerating to zero velocity
public static float EaseOutCubic(float t, float start, float length)
{
return length * ((t = t - 1) * t * t + 1) + start;
}
// cubic easing in/out - acceleration until halfway, then deceleration
public static float EaseInOutCubic(float t, float start, float length)
{
if ((t / 2) < 1) return length / 2 * t * t * t + start;
return length / 2 * ((t -= 2) * t * t + 2) + start;
}
///////////// QUARTIC EASING: t^4 /////////////////////
// quartic easing in - accelerating from zero velocity
public static float EaseInQuart(float t, float start, float length)
{
return length * (t) * t * t * t + start;
}
// quartic easing out - decelerating to zero velocity
public static float EaseOutQuart(float t, float start, float length)
{
return -length * ((t = t - 1) * t * t * t - 1) + start;
}
// quartic easing in/out - acceleration until halfway, then deceleration
public static float EaseInOutQuart(float t, float start, float length)
{
if ((t / 2) < 1) return length / 2 * t * t * t * t + start;
return -length / 2 * ((t -= 2) * t * t * t - 2) + start;
}
///////////// QUINTIC EASING: t^5 ////////////////////
// quintic easing in - accelerating from zero velocity
public static float EaseInQuint(float t, float start, float length)
{
return length * (t) * t * t * t * t + start;
}
// quintic easing out - decelerating to zero velocity
public static float EaseOutQuint(float t, float start, float length)
{
return length * ((t = t - 1) * t * t * t * t + 1) + start;
}
// quintic easing in/out - acceleration until halfway, then deceleration
public static float EaseInOutQuint(float t, float start, float length)
{
if ((t / 2) < 1) return length / 2 * t * t * t * t * t + start;
return length / 2 * ((t -= 2) * t * t * t * t + 2) + start;
}
public static float Sin(float t, float min, float max)
{
return (float)Math.Sin(t * twoPi) * (max - min) + min;
}
public static float Cos(float t, float min, float max)
{
return (float)Math.Cos(t * twoPi) * (max - min) + min;
}
public static float Sin(float t, float min, float max, float period)
{
return (float)Math.Sin(t * twoPi * period) * (max - min) + min;
}
public static float Cos(float t, float min, float max, float period)
{
return (float)Math.Cos(t * twoPi * period) * (max - min) + min;
}
///////////// SINUSOIDAL EASING: sin(t) ///////////////
// sinusoidal easing in - accelerating from zero velocity
public static float EaseInSine(float t, float start, float length)
{
return -length * (float)Math.Cos(pi / 2) + length + start;
}
// sinusoidal easing out - decelerating to zero velocity
public static float EaseOutSine(float t, float start, float length)
{
return length * (float)Math.Sin(pi / 2) + start;
}
// sinusoidal easing in/out - accelerating until halfway, then decelerating
public static float EaseInOutSine(float t, float start, float length)
{
return -length / 2 * ((float)Math.Cos(pi * t) - 1) + start;
}
///////////// EXPONENTIAL EASING: 2^t /////////////////
// exponential easing in - accelerating from zero velocity
public static float EaseInExpo(float t, float start, float length)
{
return (t == 0) ? start : length * (float)Math.Pow(2, 10 * (t - 1)) + start;
}
// exponential easing out - decelerating to zero velocity
public static float EaseOutExpo(float t, float start, float length)
{
return (t == 1) ? start + length : length * (-(float)Math.Pow(2, -10 * t) + 1) + start;
}
// exponential easing in/out - accelerating until halfway, then decelerating
public static float EaseInOutExpo(float t, float start, float length)
{
if (t == 0) return start;
if (t == 1) return start + length;
if ((t / 2) < 1) return length / 2 * (float)Math.Pow(2, 10 * (t - 1)) + start;
return length / 2 * (-(float)Math.Pow(2, -10 * --t) + 2) + start;
}
/////////// CIRCULAR EASING: sqrt(1-t^2) //////////////
// circular easing in - accelerating from zero velocity
public static float EaseInCirc(float t, float start, float length)
{
return -length * ((float)Math.Sqrt(1 - (t) * t) - 1) + start;
}
// circular easing out - decelerating to zero velocity
public static float EaseOutCirc(float t, float start, float length)
{
return length * (float)Math.Sqrt(1 - (t = t - 1) * t) + start;
}
// circular easing in/out - acceleration until halfway, then deceleration
public static float EaseInOutCirc(float t, float start, float length)
{
if ((t / 2) < 1) return -length / 2 * ((float)Math.Sqrt(1 - t * t) - 1) + start;
return length / 2 * ((float)Math.Sqrt(1 - (t -= 2) * t) + 1) + start;
}
/////////// ELASTIC EASING: exponentially decaying sine wave //////////////
/// <summary>
/// EaseInElastic
/// </summary>
/// <param name="t">current time</param>
/// <param name="start">beginning value</param>
/// <param name="length">change in value</param>
/// <param name="duration">duration</param>
/// <param name="a">amplitude (optional)</param>
/// <param name="p">period (optional)</param>
/// <returns>equation result</returns>
public static float EaseInElastic(float t, float start, float length, params float[] ampl_period)
{
if (t == 0) return start;
if ((t) == 1) return start + length;
float p = ampl_period.Length > 1 ? ampl_period[1] : .3f;
float a = ampl_period.Length > 0 ? ampl_period[0] : 0;
float s;
if (a < Math.Abs(length))
{
a = length;
s = p / 4;
}
else
{
s = p / (2 * pi) * (float)Math.Asin(length / a);
}
return -(a * (float)Math.Pow(2, 10 * (t -= 1)) * (float)Math.Sin((t - s) * (2 * pi) / p)) + start;
}
/// <summary>
/// EaseOutElastic
/// </summary>
/// <param name="t">current time</param>
/// <param name="start">beginning value</param>
/// <param name="length">change in value</param>
/// <param name="duration">duration</param>
/// <param name="a">amplitude (optional)</param>
/// <param name="p">period (optional)</param>
/// <returns>equation result</returns>
public static float EaseOutElastic(float t, float start, float length, params float[] ampl_period)
{
if (t == 0) return start;
if ((t) == 1) return start + length;
float p = ampl_period.Length > 1 ? ampl_period[1] : .3f;
float a = ampl_period.Length > 0 ? ampl_period[0] : 0;
float s;
if (a < (float)Math.Abs(length))
{
a = length;
s = p / 4;
}
else
{
s = p / (2 * pi) * (float)Math.Asin(length / a);
}
return a * (float)Math.Pow(2, -10 * t) * (float)Math.Sin((t - s) * (2 * pi) / p) + length + start;
}
/// <summary>
/// EaseInOutElastic
/// </summary>
/// <param name="t">current time</param>
/// <param name="start">beginning value</param>
/// <param name="length">change in value</param>
/// <param name="duration">duration</param>
/// <param name="a">amplitude (optional)</param>
/// <param name="p">period (optional)</param>
/// <returns>equation result</returns>
public static float EaseInOutElastic(float t, float start, float length, params float[] ampl_period)
{
if (t == 0) return start;
if ((t / 2) == 2) return start + length;
float p = ampl_period.Length > 1 ? ampl_period[1] : .3f * 1.5f;
float a = ampl_period.Length > 0 ? ampl_period[0] : 0;
float s;
if (a < (float)Math.Abs(length))
{
a = length;
s = p / 4;
}
else
{
s = p / (2 * pi) * (float)Math.Asin(length / a);
}
if (t < 1)
{
return -.5f * (float)(a * Math.Pow(2, 10 * (t -= 1)) * Math.Sin((t - s) * (2 * Math.PI) / p)) + start;
}
return a * (float)Math.Pow(2, -10 * (t -= 1)) * (float)Math.Sin((t - s) * (2 * pi) / p) * .5f + length + start;
}
/////////// BACK EASING: overshooting cubic easing: (s+1)*t^3 - s*t^2 //////////////
// back easing in - backtracking slightly, then reversing direction and moving to target
public static float EaseInBack(float t, float start, float length, params float[] sval)
{
float s = sval.Length == 0 ? 1.70158f : sval[0];
return length*t*t*((s+1)*t - s) + start;
}
// back easing out - moving towards target, overshooting it slightly, then reversing and coming back to target
public static float EaseOutBack(float t, float start, float length, params float[] sval)
{
float s = sval.Length == 0 ? 1.70158f : sval[0];
return length*((t=t-1)*t*((s+1)*t + s) + 1) + start;
}
// back easing in/out - backtracking slightly, then reversing direction and moving to target,
public static float EaseInOutBack(float t, float start, float length, params float[] sval)
{
float s = sval.Length == 0 ? 1.70158f : sval[0];
if ((t / 2) < 1)
{
return length / 2 * (t * t * (((s *= (1.525f)) + 1) * t - s)) + start;
}
return length / 2 * ((t -= 2) * t * (((s *= (1.525f)) + 1) * t + s) + 2) + start;
}
/////////// BOUNCE EASING: exponentially decaying parabolic bounce //////////////
// bounce easing in
public static float EaseInBounce(float t, float start, float length)
{
return length - EaseOutBounce(1 - t, 0, length) + start;
}
// bounce easing out
public static float EaseOutBounce(float t, float start, float length)
{
if ((t) < (1 / 2.75f))
{
return length * (7.5625f * t * t) + start;
}
else if (t < (2 / 2.75))
{
return length * (7.5625f * (t -= (1.5f / 2.75f)) * t + .75f) + start;
}
else if (t < (2.5 / 2.75))
{
return length * (7.5625f * (t -= (2.25f / 2.75f)) * t + .9375f) + start;
}
else
{
return length * (7.5625f * (t -= (2.625f / 2.75f)) * t + .984375f) + start;
}
}
// bounce easing in/out
public static float EaseInOutBounce(float t, float start, float length)
{
if (t < .5f)
{
return EaseInBounce(t * 2f, 0, length) * .5f + start;
}
return EaseOutBounce(t, 0, length) * .5f + length * .5f + start;
}
}
}
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.CoreAnimation;
using System.Timers;
using V2DRuntime.Tween;
namespace Example_CoreAnimation
{
public partial class LittleBounceViewController : UIViewController
{
/* ommitting the rest of the implemenation code */
void transitionIn ()
{
UIView.AnimationsEnabled = false;
var localMediaTime = CAAnimation.CurrentMediaTime();
NSObject[] keyframes = TweenBuilder.CreateKeyValues(-295, 0, Easing.EaseOutBounce);
var homeIn = new CAKeyFrameAnimation {
KeyPath = "position.x",
Duration = 1.4,
BeginTime = localMediaTime,
FillMode = CAFillMode.Forwards,
RemovedOnCompletion = false,
TimingFunction = CAMediaTimingFunction.FromName( CAMediaTimingFunction.Linear ),
Values = keyframes
};
navHome.AddAnimation( homeIn, "homeIn" );
var aboutIn = new CAKeyFrameAnimation {
KeyPath = "position.x",
Duration = 1.4,
BeginTime = localMediaTime + 0.3,
FillMode = CAFillMode.Forwards,
RemovedOnCompletion = false,
TimingFunction = CAMediaTimingFunction.FromName( CAMediaTimingFunction.Linear ),
Values = keyframes
};
navAbout.AddAnimation( aboutIn, "aboutIn" );
var connectIn = new CAKeyFrameAnimation {
KeyPath = "position.x",
Duration = 1.4,
BeginTime = localMediaTime + 0.6,
FillMode = CAFillMode.Forwards,
RemovedOnCompletion = false,
TimingFunction = CAMediaTimingFunction.FromName( CAMediaTimingFunction.Linear ),
Values = keyframes
};
navConnect.AddAnimation( connectIn, "connectIn" );
}
}
}
using System;
using MonoTouch.Foundation;
namespace Example_CoreAnimation
{
public delegate float EasingFormula(float t, float start, float length);
public class TweenBuilder
{
public static NSObject[] CreateKeyValues (float fromValue, float toValue, EasingFormula easingFormula, int steps = 100)
{
NSObject[] values = new NSObject[steps];
double value = 0;
float curTime = 0;
for (int t = 0; t < steps; t++) {
curTime = (float)t / (float)steps;
var easingFactor = easingFormula(curTime, 0, 1);
value = (toValue - fromValue) * easingFactor + fromValue;
values[t] = NSNumber.FromDouble(value);
}
return values;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment