A hardware-accelerated animation library for mobile and desktop.
Clone or download
Pull request Compare This branch is 3 commits ahead of millennialmedia:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
dist
doc
js
tasks
test
.gitignore
.npmignore
.npmrc
.travis.yml
CHANGELOG.md
Gruntfile.js
LICENSE.txt
README.md
package.json

README.md

jquery.hx travis-ci

A hardware-accelerated animation library for mobile and desktop.

Overview

hx is a JavaScript animation library that couples the slick animation capabilities of CSS3 with the power and flexibility of JS, making complex animation sequences a breeze. It's written as a jQuery plugin and follows the familiar syntax:

$('selector').hx( arguments );

=====

Contents

=====

The Basics

Beans & Pods

The hx method accepts a single transformation object, or bean:

$('selector').hx({
    ...
});

as well as an array of beans, or pod:

$('selector').hx([
    { ... },
    { ... }
]);
  • Pods execute synchronously, meaning each pod in the queue will not run until the pod before it has been resolved. A pod will be resolved once all of its beans have been resolved.
  • By default, beans of the same type execute synchronously. Within a pod, each beana will not run until the beana before it has been resolved.
  • Beans of different types execute asynchronously. Within a pod, beana and beanb can run simultaneously.

It's important to note that passing a transformation to the hx method will always create a pod. In the following snippet, the transform and opacity beans will execute simultaneously because they are in the same pod:

$('selector').hx([
    {
        type: 'transform'
    },
    {
        type: 'opacity'
    }
]);

However, if we separate the beans into two hx calls, the second pod will not execute until the first pod is resolved:

$('selector')
.hx({
    type: 'transform'
})
.hx({
    type: 'opacity'
});

Queueing

Each time a synchronous hx method is called, a pod is pushed to a queue for each element returned by 'selector'. Each element has its own queue which executes independently. This allows us to do things like:

$('selector1').hx({
    ...
    duration: 400
});

$('selector2').hx({
    ...
    duration: 800
});

$('selector3').hx({
    ...
    duration: 1200
});

$('selector1, selector2, selector3').hx( 'done' , function() {
    // this function will be executed after 1200ms
});

The following diagram illustrates how the queues for each element in the previous example will be executed. Since we used the selector 'selector1, selector2, selector3', the done function will not run until all of the associated promise pods have been resolved:

hx queue

  1. An animation pod is pushed to each queue.
  2. Promise pods associated with the done function are pushed to each queue.
  3. As each animation pod finishes running, the promise pod that follows it will be resolved.
  4. Once all of the promise pods have been resolved, the promise function is executed.

Promises

hx is bundled with wee-promise, so it will work even in browsers that have not yet implemented promises. If you're not familiar with the concept of promises, you may find these resources helpful:

=====

Animations

General Syntax

Beans

Every hx animation bean will fall into one of two categories:

  1. Style types with a single value (like opacity)

    {
        type: String
        value: Variant
    }
  2. Style types with multiple components (like transform or filter)

    {
        type: String
        component1: Variant
        component2: Variant
        ...
    }

Pods

A pod must contain at least one bean:

[
    {bean-0},
    {bean-1},
    ...
    {bean-n}
]

A pod will also accept timing callbacks that are executed while the pod is running:

[
    function-0,
    function-1,
    ...
    function-n
]

Two arguments are passed to each timing callback:

  • elapsed is the time (ms) that has elapsed since the pod started running.
  • progress is an array containing the percent completion for each bean in the pod.
function( elapsed , progress ) {
    if (progress[1] >= 0.5) {
        $(this)
        .hx()
        .detach()
        .resolve( true );
    }
}
  • NOTE: Timing callbacks will continue to run even after a pod is resolved or canceled unless detach is called.

Operators, Values, and Persistent States

Assignment operators (+=, -=, *=, /=, and %=) can be used to perform relative changes:

$('selector').hx({
    type: 'transform',
    rotateZ: '-=1',
    translate: {y: '+=1'}
});

A numeric value represents an absolute transform:

$('selector').hx({
    type: 'transform',
    translate: {y: 100}
});

All properties will also accept a function that returns the appropriate argument:

$('selector').hx({
    type: 'transform',
    translate: function( element , i ) {
        return {y: (i * 50)};
    }
});

Style information will persist until the property is reset:

$('selector').hx({
    type: 'transform',
    translate: {x: 50}
});

// some time later...

$('selector').hx({
    type: 'transform',
    translate: {y: 100}
});

// the translate.x component persists
// so the element is translated to (50,100,0)

A property can be reset by setting it to null:

$('selector').hx({
    type: 'transform',
    translate: null
});

// the element is translated to (0,0,0)

When a property is reset, it is removed from the style string. To force default values to be written, pass an empty object or string, depending on the type of arguments that property requires:

$('selector').hx([
    {
        type: 'opacity',
        value: ''
    },
    {
        type: 'transform',
        translate: {},
        rotateZ: ''
    }
]);

// opacity: 1; transform: translate3d(0,0,0) rotateZ(0);
// is written to the element's style string
  • NOTE: For properties like translate that require an object, this only works if the property does not have any stored values. Otherwise, you must explicitly pass the defaults, i.e. {x: 0, y: 0, z: 0}.

Predefined Style Properties

Type Property Defaults Hardware-Accelerated
transform translate {x: 0, y: 0, z: 0} YES
transform scale {x: 1, y: 1, z: 1} YES
transform rotate {x: 0, y: 0, z: 0, a: 0} YES
transform rotateX 0 YES
transform rotateY 0 YES
transform rotateZ 0 YES
transform translate2d {x: 0, y: 0} NO
transform scale2d {x: 1, y: 1} NO
opacity n/a 1 NO

animate vs. iterate

As of version 1.0.3, hx includes two animation methods:

  • animate is the default animation method. Whenever $('selector').hx({ ... }) is called, the animation will be performed using this method. animate uses CSS transitions, making it much lighter but subject to the same constraints as CSS animations.

  • iterate attempts to update the DOM at 60 fps, making it heavier but free of CSS animation constraints. For example, an element can be translated and scaled simultaneously with different durations and easings.

animate iterate
Core CSS Transitions requestAnimationFrame
Bean Execution Beans of the same type are synchronous ALL beans are asynchronous
Resource Consumption Low High
Pause / Resume No Yes
Real Time Position No Yes

=====

Options

Parameters

The following options can be included in each bean:

Name Type Description Default
duration Integer
Function
The transition duration (ms) 400
delay Integer
Function
The transition delay (ms) 0
easing String
Array
Function
The transition easing 'ease'
order Array An array setting the order of properties for the bean type (see Troubleshooting: Transform Order) []
done Function A function to be executed on bean completion null
ref String
Function
A reference string for this bean null
  • If duration, delay, or easing are passed as functions, they are evaluated immediately.
$('selector').hx({
    type: 'transform',
    translate: {y: '+=100'},
    duration: function( element , i ) {
        return $(element).hasClass( 'slow' ) ? 600 : 300;
    },
    delay: function( element , i ) {
        return i * 50;
    },
    ref: function( element , i ) {
        return '#maybe-unique-bean-ref-' + i;
    }
});

Easing

linear ease ease-in ease-out
ease-in-out easeInQuad easeInCubic easeInQuart
easeInQuint easeInSine easeInExpo easeInCirc
easeInBack* easeOutQuad easeOutCubic easeOutQuart
easeOutQuint easeOutSine easeOutExpo easeOutCirc
easeOutBack* easeInOutQuad easeInOutCubic easeInOutQuart
easeInOutQuint easeInOutSine easeInOutExpo easeInOutCirc
easeInOutBack* easeOutBackMod1* easeMod1 easeMod2
gravityUp gravityDown custom

*Bezier curves with values above 1 or below 0 are not compatible on all devices. See WebKit Bug 45761.

  • hx will check unclamped bezier compatibility and clamp the points between 0 and 1 if necessary.
  • Custom easing can be defined when the page loads using $.hx.defineBezier, or passed directly as an array of four points:
$('selector').hx({
    ...
    easing: [ 0.17 , 0.67 , 0.38 , 0.67 ],
    ...
});

=====

Methods

Overview

.hx([ method , args ])

  • Invokes .animate() if the first argument is a bean or pod, or calls another method if the first argument is a string.
  • .hx() is the only method available to jquery, so in order call another hx method (like defer), you must either create a new hx instance first or pass the name of the method as the first argument of .hx(). The following examples both show valid ways to call .defer( 1000 ):
  1. Chaining to an hx instance

    $('selector').hx().defer( 1000 );
  2. Calling the method directly

    $('selector').hx( 'defer' , 1000 );

Static Methods

$.hx.defineProperty( name , [ realName ])

Parameter Type Description Required
name String The name by which you will reference this property YES
realName String The actual name of the CSS property NO
  • Defines a new style property, or throws an error if name already exists.
  • Returns a new StyleDefinition instance:
Property Defaults
defaults [ '' ]
keymap [ 0 ]
stringGetter function( name , CSSProperty ) { return CSSProperty[0] }
// define some style properties for CSS filter

$.hx.defineProperty( 'blur' )
    .set( 'defaults' , 0 )
    .set( 'stringGetter' , function( name , CSSProperty ) {
        return name + '(' + CSSProperty[0] + 'px)';
    });

$.hx.defineProperty( 'dropShadow' , 'drop-shadow' )
    .set( 'defaults' , [ 0 , 0 , 0 , 'transparent' ])
    .set( 'keymap' , [ 'x' , 'y' , 'blur' , 'color' ])
    .set( 'stringGetter' , function( name , CSSProperty ) {
        return name + '(' + CSSProperty.join( 'px ' ) + ')';
    });

// now use them
$('selector').hx({
    type: 'filter',
    blur: 2,
    dropShadow: {x: 10, y: 10, color: 'blue'}
});

$.hx.defineBezier( name , points )

Parameter Type Description Required
name String The name of your easing function YES
points Array[4] Four points defining a cubic bezier spline YES
  • Defines a new easing function, or throws an error if name already exists.
$.hx.defineBezier( 'someEasing' , [ 0.25 , 0.1 , 0.25 , 1 ]);

$.hx.subscribe( callback )

Parameter Type Description Required
callback Function The timing callback YES
  • Subscribes to the hx timing module.
var unsubscribe = $.hx.subscribe(function( elapsed ) {
    if (elapsed >= targetTime) {
        unsubscribe();
    }
    else {
        // do something
    }
});

$.hx.error( error )

Parameter Type Description Required
error Error The error thrown within an hx promise function n/a
  • The function to be executed when an error is thrown within an hx promise function.
  • Errors thrown within promise functions will not propagate to the window. Without this function, hx chains will fail silently when an error is encountered (see MDN Promise Documentation).
  • $.hx.error can be overridden to suit your error handling needs.
// the default $.hx.error function
$.hx.error = function( error ) {
    try { console.error( error.stack ); }
    catch( err ) {}
};

// override
$.hx.error = function( error ) {
    alert(error.stack);
};

Synchronous Methods

.animate( obj )

Parameter Type Description Required
obj Object A bean or pod YES
  • Chainable: YES
  • Runs an animation using CSS transitions.
$('selector').hx({
    type: 'transform',
    ...
});

// is the same as:
$('selector').hx( 'animate' , {
    type: 'transform',
    ...
});

.iterate( obj )

Parameter Type Description Required
obj Object A bean or pod YES
  • Chainable: YES
  • Runs an animation by iteratively updating the DOM.
$('selector').hx( 'iterate' , {
    type: 'transform',
    ...
});

.defer([ time ])

Parameter Type Description Required
time Integer The amount of time (ms) to defer queue execution NO
  • Chainable: YES
  • Prevents the queue from executing for a set amount of time, or until resolve is called.
$('selector')
.hx( 'defer' , 500 )
.hx({
    ... // this pod will run after 500ms
})
.defer()
.hx({
    ... // this pod won't run yet
});

// some time later...
$('selector').hx( 'resolve' );
// now the last pod will run

.then( callback )

Parameter Type Description Required
callback Function The promise function YES
  • Chainable: YES
  • resolve allows the queue to continue.
  • reject stops execution and clears the queue.
  • NOTE: failing to resolve or reject the promise created by then will cause a queue jam.
$('selector1, selector2')
.hx({
    ...
})
.then(function( resolve , reject ) {
    // this function runs when all
    // elements finish their animations
    if (awesome) {
        resolve();
    } else {
        reject();
    }
})
.hx({
    ...
    // this pod runs if the promise is resolved
    // if the promise is rejected, the queue is cleared
    // and this pod is not executed
});

.race( callback )

Parameter Type Description Required
callback Function The promise function YES
  • Chainable: YES
  • resolve allows the queue to continue.
  • reject stops execution and clears the queue.
  • NOTE: failing to resolve or reject the promise created by race will cause a queue jam.
$('selector1, selector2')
.hx({
    ...
    duration: function() {
        return (1000 * Math.random());
    }
})
.race(function( resolve , reject ) {
    // this function runs when the first
    // element finishes its animation
    resolve();
});

$('selector1').hx({
    ... // this pod runs when race is resolved
});

.done([ callback ])

Parameter Type Description Required
callback Function The promise function NO
  • Chainable: NO
  • done performs the same way as then, but does not create a promise that needs to be resolved. It is intended for use at the end of an animation chain.
  • callback is ensured before it's executed, so passing an undefined callback to done will not cause an error to be thrown.
$('selector1, selector2')
.hx({
    ...
    duration: function() {
        return (1000 * Math.random());
    }
})
.done(function() {
    // it's done!
});

Asynchronous Methods

.clear()

  • Chainable: YES
  • Clears all pods in the queue.
$('selector').hx( 'clear' );

.break()

  • Chainable: YES
  • Clears all but the current pod in the queue.
$('selector').hx( 'break' );

.detach()

  • Chainable: YES
  • Detaches timing and paint callbacks from the current pod, but allows it to continue running.
$('selector').hx( 'detach' );

.pause()

  • Chainable: YES
  • Pauses the current animation if it was run using iterate.
$('selector').hx( 'pause' );

.resume()

  • Chainable: YES
  • Resumes a paused animation.
$('selector').hx( 'resume' );

.resolve([ all ])

Parameter Type Description Required
all Boolean Denotes which pod types should be resolved NO
  • Chainable: YES
  • Resolves the current promise pod in the queue. If all is true, the current pod will be resolved regardless of type.
$('selector')
.hx({
    ... // this pod will be resolved before it is complete
})
.hx({
    ...
});

// jump to the next pod
$('selector').hx( 'resolve' , true );

.update( obj )

Parameter Type Description Required
obj Object A bean or pod YES
  • Chainable: YES
  • Updates an element's stored style information without writing to the DOM.
$('selector').hx( 'update' , {
    type: 'transform',
    translate: null
});

.reset([ type ])

Parameter Type Description Required
type String
Array
The style type(s) to be reset. NO
  • Chainable: YES
  • Clears an element's stored style information without writing to the DOM.
$('selector').hx( 'reset' );

.get([ search ])

Parameter Type Description Required
search String The style component to be retrieved. NO
  • Chainable: NO
  • Returns an array containing stored style information for each selected element.
  • If search is omitted, an object containing all stored style information is returned.
  • If search is a multi-value type (like transform):
    • An object containing each stored property for that type (if any) is returned.
  • If search is a property (like translate) or a single-value type (like opacity):
    • If the property is found, it's values are returned.
    • If the property is defined but NOT found, its defaults are returned.
    • If the property is NOT defined, an empty object is returned.
  • Detailed example here.
$('selector').hx( 'get' );

.paint([ type ])

Parameter Type Description Required
type String
Array
The style type(s) to be painted. NO
  • Chainable: YES
  • Writes an element's stored style information to the DOM.
$('selector').hx( 'paint' );

.zero( obj )

Parameter Type Description Required
obj Object A bean or pod YES
  • Chainable: YES
  • Applies a zero-duration transform.
  • zero should be used to apply multiple transforms in rapid succession (like dragging an element).
$('selector').hx( 'zero' , {
    translate: {
        x: ('+=' + delta.x),
        y: ('+=' + delta.y)
    }
});

.cleanup()

  • Chainable: NO
  • Removes the hx module from the DOM node.
$('selector').hx( 'cleanup' );

=====

Events

hx event names follow the pattern hx.namespace. You should subscribe to hx events using jquery .on().

$(target).on( 'hx.namespace' , function( e , [ data ]) {
    // ...
});
Namespace Target Data Description
ready document none Triggered when hx loads.
start element {bean: bean, ref: String} Triggered on bean start.
end element {bean: bean, ref: String} Triggered on bean end.
reject element reject arguments Triggered when a promise pod is rejected.
pause element {progress: Array} Triggered when an iteration pod is paused.
resume element {progress: Array} Triggered when an iteration pod is resumed.
error document error Triggered when an error is encountered within a promise function.

=====

Troubleshooting

Transform Order

The order in which transforms are applied will affect the final outcome. The following snippet will actually translate the target by 200px because scale is being applied first:

$('selector').hx({
    type: 'transform',
    scale: {x: 2, y: 2}
});

// some time later...

$('selector').hx({
    type: 'transform',
    translate: {x: 100, y: 100}
});

To correct this issue, order can be passed as a property of the second bean:

$('selector').hx({
    type: 'transform',
    scale: {x: 2, y: 2}
});

// some time later...

$('selector').hx({
    type: 'transform',
    translate: {x: 100, y: 100},
    order: [ 'translate' , 'scale' ]
});

Queue Jams

Queue jams are caused by an unresolved pod in the queue preventing subsequent pods from being executed. To resolve a single pod:

// resolve the current pod if it's a promise
$('selector').hx( 'resolve' );

// resolve the current pod regardless of type
$('selector').hx( 'resolve' , true );

Or, to clear the entire queue:

$('selector').hx( 'clear' );

=====

Compatibility

hx is supported in both mobile and desktop versions of all major browsers including Chrome, Safari, Firefox, Opera, and Internet Explorer 9+.

=====

Dependencies

hx requires jQuery 1.7.0 or higher.

=====

Build Instructions

You must have NPM installed to build jquery.hx. To install dependencies, navigate to the git directory and run:

npm install

To build the minified production version, run:

grunt

To build the non-minified development version, run:

grunt dev