44 views

Skip to first unread message

Apr 24, 2009, 5:37:14 AM4/24/09

to Prototype & script.aculo.us

Just thought this might be of interest to someone else. Comments and

optimizations are welcome.

//--------snip----snip----snip----snip-------------

//

==============================================================================

// Robert Penner's easing functions v2.0 (http://www.robertpenner.com/

easing)

// Ported to Scriptaculous 1.8 by Riccardo De Agostini (lozioric AT

gmail.com)

//

// Original terms of use (http://www.robertpenner.com/

easing_terms_of_use.html)

// also apply to this modification.

//

==============================================================================

//

// Penner's functions take a minimum of four parameters named t, b, c

and d,

// plus, in some cases, optional customization parameters (for

details, see

// http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf)

//

// Scriptaculous' transitions are a simplified case of Penner's

functions,

// where b is always 0, c is always 1, and d is always 1. I've thus

simplified

// the original ActionScript code.

// I've also added some transformation functions, which can take an

easeIn, an

// easeOut or an easeIn / easeOut pair and turn them into a complete

set of

// transition functions. This obviously introduces some overhead, but

greatly

// simplifies the code.

//

// Simple usage example:

//

// new Effect.Move(myElement, {

// transition: Effect.Transitions.Cubic.easeInOut

// });

//

// Customization parameters, where present, may be used as follows:

//

// // No customization (use Penner's default value)

// new Effect.Move(myElement, {

// transition: Effect.Transitions.Back.easeIn

// });

//

// // Customized easing

// new Effect.Move(myElement, {

// transition: Effect.Transitions.Back.easeIn.custom(2.5)

// });

//

//

==============================================================================

Object.extend(Effect.Transitions, (function() {

//----------------------------------------------------------------------

// Function transformations

//----------------------------------------------------------------------

// easeIn to easeOut and vice versa

function reverse(eq, t)

{

return 1 - eq(1 - t);

}

// easeIn to easeInOut

function easeInToEaseInOut(easeIn, t)

{

t = 2 * t;

return 0.5 * (t < 1 ? easeIn(t) : 2 - easeIn(2 - t));

}

// easeOut to easeInOut

function easeOutToEaseInOut(easeOut, t)

{

t = 2 * t;

return 0.5 * (t < 1 ? 1 - easeOut(1 - t) : 1 + easeOut(t -

1));

}

// easeIn / easeOut pair to easeInOut

function easeInOutPairToEaseInOut(easeIn, easeOut, t)

{

t = 2 * t;

return 0.5 * (t < 1 ? easeIn(t) : 1 + easeOut(t - 1));

}

//----------------------------------------------------------------------

// Function set builders

//----------------------------------------------------------------------

// Build a function set from a complete set of easing functions

function functionSet(easeIn, easeOut, easeInOut)

{

return {

easeIn : easeIn,

easeOut : easeOut,

easeInOut: easeInOut

};

}

// Build a complete function set from just an easeIn

function functionSetFromEaseIn(easeIn)

{

return {

easeIn : easeIn,

easeOut : reverse.curry(easeIn),

easeInOut: easeInToEaseInOut.curry(easeIn)

};

}

// Build a complete function set from just an easeOut

function functionSetFromEaseOut(easeOut)

{

return {

easeIn : reverse.curry(easeOut),

easeOut : easeOut,

easeInOut: easeOutToEaseInOut.curry(easeOut)

};

}

// Build a complete function set from an easeIn / easeOut pair

function functionSetFromEaseInOutPair(easeIn, easeOut)

{

return {

easeIn : easeIn,

easeOut : easeOut,

easeInOut: easeInOutPairToEaseInOut.curry(easeIn, easeOut)

};

}

// Build a complete function set from just an easeIn,

// where the given function has custom parameters

function customizableFunctionSetFromEaseIn()

{

var args = $A(arguments);

var easeIn = args.shift();

function customEaseIn()

{

var args = [0].concat($A(arguments));

return function(t)

{

args[0] = t;

return easeIn.apply(this, args);

};

}

function customEaseOut()

{

return reverse.curry(customEaseIn.apply(this, arguments));

}

function customEaseInOut()

{

return easeInToEaseInOut.curry(customEaseIn.apply(this,

arguments));

}

var myEaseIn = customEaseIn.apply(this, args);

myEaseIn.custom = customEaseIn;

var myEaseOut = reverse.curry(myEaseIn);

myEaseOut.custom = customEaseOut;

var myEaseInOut = easeInToEaseInOut.curry(myEaseIn);

myEaseInOut.custom = customEaseInOut;

return {

easeIn : myEaseIn,

easeOut : myEaseOut,

easeInOut: myEaseInOut

};

}

//----------------------------------------------------------------------

// Penner's tween equations, simplified for Scriptaculous

//----------------------------------------------------------------------

function Quad_easeIn(t)

{

return t * t;

}

function Cubic_easeIn(t)

{

return t * t * t;

}

function Quart_easeIn(t)

{

return t * t * t * t;

}

function Quint_easeIn(t)

{

return t * t * t * t * t;

}

// This one is not Penner's: it's just a generalized case, for use

when

// i.e. Quad is too "soft" for your tastes but Cubic is too

"quick"

// (in this specific case you could use Pow.custom(2.5) for

example)

function Pow_easeIn(t, p)

{

return Math.pow(t, p);

}

function Back_easeIn(t, s)

{

return t * t * ((s + 1) * t - s);

}

// TODO (maybe): customize (customizableize? :-) ) this one

function Bounce_easeOut(t)

{

if (t < (1 / 2.75))

return 7.5625 * t * t;

if (t < (2 / 2.75))

return 7.5625 * (t-= (1.5 / 2.75)) * t + 0.75;

if (t < (2.5 / 2.75))

return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;

return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;

}

function Circ_easeIn(t)

{

return -1 * (Math.sqrt(1 - t * t) - 1);

}

function Circ_easeOut(t)

{

t -= 1;

return Math.sqrt(1 - t * t);

}

function Elastic_easeIn(t, a, p)

{

if (t == 0) return 0;

if (t == 1) return 1;

if (a < 1)

{

a = 1;

var s = p / 4;

}

else

{

var s = p / (2 * Math.PI) * Math.asin(1 / a);

}

return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) *

(2 * Math.PI) / p));

}

function Expo_easeIn(t)

{

return (t == 0) ? 0 : Math.pow(2, 10 * (t - 1));

}

function Expo_easeOut(t)

{

return (t == 1) ? 1 : 1 - Math.pow(2, -10 * t);

}

function Sine_easeIn(t)

{

return -1 * Math.cos(t * (Math.PI / 2)) + 1;

}

function Sine_easeOut(t)

{

return 1 * Math.sin(t * (Math.PI / 2));

}

function Sine_easeInOut(t)

{

return -0.5 * (Math.cos(Math.PI * t) - 1);

}

//----------------------------------------------------------------------

// Build and return the equation sets

//----------------------------------------------------------------------

return {

Quad : functionSetFromEaseIn(Quad_easeIn),

Cubic : functionSetFromEaseIn(Cubic_easeIn),

Quart : functionSetFromEaseIn(Quart_easeIn),

Quint : functionSetFromEaseIn(Quint_easeIn),

Pow : customizableFunctionSetFromEaseIn(Pow_easeIn, 2), //

Defaults to Quad

Back : customizableFunctionSetFromEaseIn(Back_easeIn,

1.70158),

Bounce : functionSetFromEaseOut(Bounce_easeOut),

Circ : functionSetFromEaseInOutPair(Circ_easeIn,

Circ_easeOut),

Elastic: customizableFunctionSetFromEaseIn(Elastic_easeIn, 1,

0.3),

Expo : functionSetFromEaseInOutPair(Expo_easeIn,

Expo_easeOut),

Sine : functionSet(Sine_easeIn, Sine_easeOut,

Sine_easeInOut)

};

})());

// EOF

//--------snip----snip----snip----snip-------------

optimizations are welcome.

//--------snip----snip----snip----snip-------------

//

==============================================================================

// Robert Penner's easing functions v2.0 (http://www.robertpenner.com/

easing)

// Ported to Scriptaculous 1.8 by Riccardo De Agostini (lozioric AT

gmail.com)

//

// Original terms of use (http://www.robertpenner.com/

easing_terms_of_use.html)

// also apply to this modification.

//

==============================================================================

//

// Penner's functions take a minimum of four parameters named t, b, c

and d,

// plus, in some cases, optional customization parameters (for

details, see

// http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf)

//

// Scriptaculous' transitions are a simplified case of Penner's

functions,

// where b is always 0, c is always 1, and d is always 1. I've thus

simplified

// the original ActionScript code.

// I've also added some transformation functions, which can take an

easeIn, an

// easeOut or an easeIn / easeOut pair and turn them into a complete

set of

// transition functions. This obviously introduces some overhead, but

greatly

// simplifies the code.

//

// Simple usage example:

//

// new Effect.Move(myElement, {

// transition: Effect.Transitions.Cubic.easeInOut

// });

//

// Customization parameters, where present, may be used as follows:

//

// // No customization (use Penner's default value)

// new Effect.Move(myElement, {

// transition: Effect.Transitions.Back.easeIn

// });

//

// // Customized easing

// new Effect.Move(myElement, {

// transition: Effect.Transitions.Back.easeIn.custom(2.5)

// });

//

//

==============================================================================

Object.extend(Effect.Transitions, (function() {

//----------------------------------------------------------------------

// Function transformations

//----------------------------------------------------------------------

// easeIn to easeOut and vice versa

function reverse(eq, t)

{

return 1 - eq(1 - t);

}

// easeIn to easeInOut

function easeInToEaseInOut(easeIn, t)

{

t = 2 * t;

return 0.5 * (t < 1 ? easeIn(t) : 2 - easeIn(2 - t));

}

// easeOut to easeInOut

function easeOutToEaseInOut(easeOut, t)

{

t = 2 * t;

return 0.5 * (t < 1 ? 1 - easeOut(1 - t) : 1 + easeOut(t -

1));

}

// easeIn / easeOut pair to easeInOut

function easeInOutPairToEaseInOut(easeIn, easeOut, t)

{

t = 2 * t;

return 0.5 * (t < 1 ? easeIn(t) : 1 + easeOut(t - 1));

}

//----------------------------------------------------------------------

// Function set builders

//----------------------------------------------------------------------

// Build a function set from a complete set of easing functions

function functionSet(easeIn, easeOut, easeInOut)

{

return {

easeIn : easeIn,

easeOut : easeOut,

easeInOut: easeInOut

};

}

// Build a complete function set from just an easeIn

function functionSetFromEaseIn(easeIn)

{

return {

easeIn : easeIn,

easeOut : reverse.curry(easeIn),

easeInOut: easeInToEaseInOut.curry(easeIn)

};

}

// Build a complete function set from just an easeOut

function functionSetFromEaseOut(easeOut)

{

return {

easeIn : reverse.curry(easeOut),

easeOut : easeOut,

easeInOut: easeOutToEaseInOut.curry(easeOut)

};

}

// Build a complete function set from an easeIn / easeOut pair

function functionSetFromEaseInOutPair(easeIn, easeOut)

{

return {

easeIn : easeIn,

easeOut : easeOut,

easeInOut: easeInOutPairToEaseInOut.curry(easeIn, easeOut)

};

}

// Build a complete function set from just an easeIn,

// where the given function has custom parameters

function customizableFunctionSetFromEaseIn()

{

var args = $A(arguments);

var easeIn = args.shift();

function customEaseIn()

{

var args = [0].concat($A(arguments));

return function(t)

{

args[0] = t;

return easeIn.apply(this, args);

};

}

function customEaseOut()

{

return reverse.curry(customEaseIn.apply(this, arguments));

}

function customEaseInOut()

{

return easeInToEaseInOut.curry(customEaseIn.apply(this,

arguments));

}

var myEaseIn = customEaseIn.apply(this, args);

myEaseIn.custom = customEaseIn;

var myEaseOut = reverse.curry(myEaseIn);

myEaseOut.custom = customEaseOut;

var myEaseInOut = easeInToEaseInOut.curry(myEaseIn);

myEaseInOut.custom = customEaseInOut;

return {

easeIn : myEaseIn,

easeOut : myEaseOut,

easeInOut: myEaseInOut

};

}

//----------------------------------------------------------------------

// Penner's tween equations, simplified for Scriptaculous

//----------------------------------------------------------------------

function Quad_easeIn(t)

{

return t * t;

}

function Cubic_easeIn(t)

{

return t * t * t;

}

function Quart_easeIn(t)

{

return t * t * t * t;

}

function Quint_easeIn(t)

{

return t * t * t * t * t;

}

// This one is not Penner's: it's just a generalized case, for use

when

// i.e. Quad is too "soft" for your tastes but Cubic is too

"quick"

// (in this specific case you could use Pow.custom(2.5) for

example)

function Pow_easeIn(t, p)

{

return Math.pow(t, p);

}

function Back_easeIn(t, s)

{

return t * t * ((s + 1) * t - s);

}

// TODO (maybe): customize (customizableize? :-) ) this one

function Bounce_easeOut(t)

{

if (t < (1 / 2.75))

return 7.5625 * t * t;

if (t < (2 / 2.75))

return 7.5625 * (t-= (1.5 / 2.75)) * t + 0.75;

if (t < (2.5 / 2.75))

return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;

return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;

}

function Circ_easeIn(t)

{

return -1 * (Math.sqrt(1 - t * t) - 1);

}

function Circ_easeOut(t)

{

t -= 1;

return Math.sqrt(1 - t * t);

}

function Elastic_easeIn(t, a, p)

{

if (t == 0) return 0;

if (t == 1) return 1;

if (a < 1)

{

a = 1;

var s = p / 4;

}

else

{

var s = p / (2 * Math.PI) * Math.asin(1 / a);

}

return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) *

(2 * Math.PI) / p));

}

function Expo_easeIn(t)

{

return (t == 0) ? 0 : Math.pow(2, 10 * (t - 1));

}

function Expo_easeOut(t)

{

return (t == 1) ? 1 : 1 - Math.pow(2, -10 * t);

}

function Sine_easeIn(t)

{

return -1 * Math.cos(t * (Math.PI / 2)) + 1;

}

function Sine_easeOut(t)

{

return 1 * Math.sin(t * (Math.PI / 2));

}

function Sine_easeInOut(t)

{

return -0.5 * (Math.cos(Math.PI * t) - 1);

}

//----------------------------------------------------------------------

// Build and return the equation sets

//----------------------------------------------------------------------

return {

Quad : functionSetFromEaseIn(Quad_easeIn),

Cubic : functionSetFromEaseIn(Cubic_easeIn),

Quart : functionSetFromEaseIn(Quart_easeIn),

Quint : functionSetFromEaseIn(Quint_easeIn),

Pow : customizableFunctionSetFromEaseIn(Pow_easeIn, 2), //

Defaults to Quad

Back : customizableFunctionSetFromEaseIn(Back_easeIn,

1.70158),

Bounce : functionSetFromEaseOut(Bounce_easeOut),

Circ : functionSetFromEaseInOutPair(Circ_easeIn,

Circ_easeOut),

Elastic: customizableFunctionSetFromEaseIn(Elastic_easeIn, 1,

0.3),

Expo : functionSetFromEaseInOutPair(Expo_easeIn,

Expo_easeOut),

Sine : functionSet(Sine_easeIn, Sine_easeOut,

Sine_easeInOut)

};

})());

// EOF

//--------snip----snip----snip----snip-------------

Apr 24, 2009, 8:32:06 AM4/24/09

to Prototype & script.aculo.us

On Apr 24, 10:37 am, Riccardo De Agostini wrote:

<snip>

language does not contain such a thing as a negative numeric literal.

Instead constructs such as the "-1" above represent the unary negation

operator being applied to the positive numeric literal one. So

theoretically "-1" implies a runtime operation, though it is certainly

possible that an ECMAScript implementation could treat it as a

negative numeric literal (as doing so would not change the behaviour

of the code) and so only act on the "-" while compiling the code.

In any event, the expression "-1 * x" should always have the same

outcome as the expression "-x" and the latter would be one fewer

operations in ECMAScript terms (and even if the -1 is treated as a

negative numeric literal the unary negation operation should be faster

than the multiplication). Granted there won't be much in it

(especially against the unavoidable overheads of the function call),

but why not.

> function Sine_easeOut(t)

> {

> return 1 * Math.sin(t * (Math.PI / 2));

> }

<snip>

I cannot see any reason for multiplying any number by positive one.

That operation is sometimes used to force type-conversion (to numeric)

but that cannot be the reason here as the return value from - Math.sin

- is already guaranteed to be numeric.

There are (inevitably) a number of uses of - Math.PI * 2 - and -

Math.PI / 2 - in this code. It might be an idea to assign these two

values to variables in the containing scope and employ them as

constants in the functions. Apart from not repeating the math

operations each time they are used, the scope-chain lookup of the

variables should never be slower than the scope-chain lookup of - Math

- and so certainly will be faster than resolving - Math.PI -.

<snip>

> function Sine_easeIn(t)

> {

> return -1 * Math.cos(t * (Math.PI / 2)) + 1;

> }

It is an underappreciated characteristic of ECMAScript that the
> {

> return -1 * Math.cos(t * (Math.PI / 2)) + 1;

> }

language does not contain such a thing as a negative numeric literal.

Instead constructs such as the "-1" above represent the unary negation

operator being applied to the positive numeric literal one. So

theoretically "-1" implies a runtime operation, though it is certainly

possible that an ECMAScript implementation could treat it as a

negative numeric literal (as doing so would not change the behaviour

of the code) and so only act on the "-" while compiling the code.

In any event, the expression "-1 * x" should always have the same

outcome as the expression "-x" and the latter would be one fewer

operations in ECMAScript terms (and even if the -1 is treated as a

negative numeric literal the unary negation operation should be faster

than the multiplication). Granted there won't be much in it

(especially against the unavoidable overheads of the function call),

but why not.

> function Sine_easeOut(t)

> {

> return 1 * Math.sin(t * (Math.PI / 2));

> }

I cannot see any reason for multiplying any number by positive one.

That operation is sometimes used to force type-conversion (to numeric)

but that cannot be the reason here as the return value from - Math.sin

- is already guaranteed to be numeric.

There are (inevitably) a number of uses of - Math.PI * 2 - and -

Math.PI / 2 - in this code. It might be an idea to assign these two

values to variables in the containing scope and employ them as

constants in the functions. Apart from not repeating the math

operations each time they are used, the scope-chain lookup of the

variables should never be slower than the scope-chain lookup of - Math

- and so certainly will be faster than resolving - Math.PI -.

Apr 26, 2009, 6:15:04 AM4/26/09

to Prototype & script.aculo.us

> In any event, the expression "-1 * x" should always have the same

> outcome as the expression "-x" and the latter would be one fewer

> operations in ECMAScript terms (and even if the -1 is treated as a

> negative numeric literal the unary negation operation should be faster

> than the multiplication).

> outcome as the expression "-x" and the latter would be one fewer

> operations in ECMAScript terms (and even if the -1 is treated as a

> negative numeric literal the unary negation operation should be faster

> than the multiplication).

> I cannot see any reason for multiplying any number by positive one.

My fault, in both cases above. The first thing I did when porting
Penner's code was sustituting 1 for all occurrences of the c and d

parameters and 0 for b, then I simplified the equations, but evidently

I forgot some 1 * <anything> here and there. I'm going to post a

revised version very soon.

> There are (inevitably) a number of uses of - Math.PI * 2 - and -

> Math.PI / 2 - in this code. It might be an idea to assign these two

> values to variables in the containing scope and employ them as

> constants in the functions.

Apr 27, 2009, 2:51:38 AM4/27/09

to Prototype & script.aculo.us

A corrected version (as well as future versions, if any) can be found

here:

http://snipplr.com/view/14458/robert-penners-actionscript-easing-functions-ported-to-scriptaculous-18/

here:

http://snipplr.com/view/14458/robert-penners-actionscript-easing-functions-ported-to-scriptaculous-18/

Reply all

Reply to author

Forward

0 new messages

Search

Clear search

Close search

Google apps

Main menu