jquery.hx
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
- Animations
- Options
- Methods
- Events
- Troubleshooting
- Compatibility
- Dependencies
- Build Instructions
=====
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:
- An animation pod is pushed to each queue.
- Promise pods associated with the
done
function are pushed to each queue. - As each animation pod finishes running, the promise pod that follows it will be resolved.
- 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:
-
Style types with a single value (like opacity)
{ type: String value: Variant }
-
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 |
- Additional properties can be defined using $.hx.defineProperty.
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
*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 )
:
-
Chaining to an hx instance
$('selector').hx().defer( 1000 );
-
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 asthen
, 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 todone
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