Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
A teeny tiny tweening engine in JavaScript. That's all it does.
JavaScript Shell
branch: master

This branch is 179 commits behind jeremyckahn:master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
builds
demos
doc
examples
src
tests
.gitignore
README.md
build.sh
index.html
shifty.js

README.md

Shifty - A teeny tiny tweening engine in JavaScript.

Shifty is a tweening engine for JavaScript. That's it. Shifty is a low-level library meant to be encapsulated by higher-level tools. At the most basic level, it provides:

  • Tweening of Numbers.
  • Extensibility hooks for the tweening.

Shifty is great because it focuses on doing one thing very well - tweening. It is optimized to run many times a second with minimal processing and memory overhead, which is necessary for smooth animations. To this end, the Shifty core doesn't do:

  • Keyframing.
  • Drawing.
  • Much else.

If you need functionality like this and more, you can easily extend or wrap Shifty's core with whatever you need. In fact, there are some extensions included in this repo to do just that. Currently, there are Shifty extensions for:

  • shifty.color.js: Color tweening (RGB/Hex strings).
  • shifty.css_units.js: CSS Unit strings ("px", "em", "%", etc.), so you can tween DOM elements.
  • shifty.queue.js: Queuing up tweens that execute sequentially.

There is also a file called shifty.formulas.js that contains a bunch of ready-to-use easing formulas, adapted from Scripty2 and Robert Penner.

Using Shifty

If you just want raw tweening functionality, all you need is shifty.core.js. This is in the src directory. Just drop that in your page, and you are ready to go. To use any Shifty extensions or additions, simply load them after shifty.core.js.

Shifty, with all extensions come prepackaged in shifty.js in the root of the project directory, and also minified in the builds directory as shifty.min.js.

API

The section explains how to use the Shifty core. For information on each extension, explore the doc/ directory.

Making a tweenable() instance

The first thing you need to do is create a new instance of Tweenable. Example:

var myTweenable = new Tweenable();

You can also supply some fun options to init() via an Object. They are:

  • fps: A Number. This is the framerate (frames per second) at which the tween animation updates. The default is 30.
  • easing: A String. The default easing formula to use on a tween. This can be overridden on a per-tween basis via the tween function's easing parameter (see below). This value is linear by default.
  • duration: A Number. The default duration that a tween lasts for. This can be overridden on a per-tween basis via the tween function's duration parameter (see below).
  • intialState: An Object. The state at which the first tween should begin at.

Starting a tween

tween()

Make a basic tween:

myTweenable.tween( from, to );

You can optionally add some fun extra parameters:

myTweenable.tween( from, to, duration, callback, easing );

The previous examples used the shorthand syntax. You can also use the longhand configuration object syntax (recommended!):

myTweenable.tween({
  from:       {  },            // Object.  Contains the properties to tween.  Must all be `Number`s.  Note: This object's properties are modified by the tween.
  to:         {  },            // Object.  The "destination" `Number`s that the properties in `from` will tween to.
  duration:   1000,            // Number.  How long the tween lasts for, in milliseconds.
  easing:     'linear',        // String or Object.  Easing equation(s) to use.  You can specify any easing method that was attached to `Tweenable.prototype.formula`.
  start:      function () {},  // Function.  Runs as soon as the tween begins.  Handy when used with the `queue` extension.
  step:       function () {},  // Function.  Runs each "frame" that the tween is updated.
  callback:   function () {}   // Function.  Runs when the tween completes.
});

This method starts a tween. You can use either format, but the second, longer format give you more hooks and controls. Tweenable also has some methods that you can use to control a tween, as described in the next section.

Important! The object that is passed as the from parameter, regardless of which syntax you use to invoke tween(), is modified.

to()

Another handy way to tween is to use the to method. to() is nearly identical to tween() - in fact, to() just wraps tween() internally. The only difference is that you can omit the from parameter. to() simply assumes that you want to tween from the Tweenable instance's current position. Like tween, the shorthand and longhand syntaxes are supported. Shorthand:

myTweenable.to( to );
myTweenable.to( to, duration, callback, easing );

Longhand:

myTweenable.to({
  to:         {  },            // Object.  The "destination" `Number`s that the properties in `from` will tween to.
  duration:   1000,            // Number.  How long the tween lasts for, in milliseconds.
  easing:     'linear',        // String or Object.  Easing equation to use.  You can specify any easing method that was attached to `Tweenable.prototype.formula`.
  start:      function () {},  // Function.  Runs as soon as the tween begins.
  step:       function () {},  // Function.  Runs each "frame" that the tween is updated.
  callback:   function () {}   // Function.  Runs when the tween completes.
});

However you call to(), the only required parameter is to.

And yes... I should probably come up with a better naming scheme, because even I'm confused by that.

Controlling a tween

Continuing from above...

myTweenable.stop( gotoEnd );

Stops a tween.

  • gotoEnd: Boolean. Controls whether to jump to the end "to" state or just stop where the tweened values currently are.
myTweenable.pause();

Pauses a tween. This is different from stop(), as you are to resume from a pause()ed state.

myTweenable.resume();

Resumes a pause()ed tween.

myTweenable.get();

Returns a Tweenable's current internal state values.

myTweenable.set(state);

Sets (and overwrites) the Tweenable instance's current internal state properties.

  • state: An Object containing the properties that the state should have. Any properties not present in this Object will be erased form the current state.

Using multiple easing formulas

You can create tweens that use different easing formulas for each property. Having multiple easing formulas on a single tween can make for some really interesting motions, because you arent constrained to moving things in a straight line. You can make curves! To do this, simply supply easing as an Object, rather than a string to tween():

myTweenable.tween({
  from: {
    x: 0,
    y: 0
  },
  to: {
    x: 250,
    y: 150
  },
  easing: {
    x: 'swingFromTo',
    y: 'bounce'
  }
});

You can use an an Object to specify the easing to use in any Tweenable method that accepts an easing parameter (on other words, you can use this with to and the Interpolate extension). Mix and match to make interesting new animations.

Extending Tweenable()

Shifty's true power comes from it's extensibility. It is designed to be an inheritable, effective base Object. A quick example of how to set that up:

function Cartoon () {
  // Borrow `Tweenable`'s constructor
  this.constructor.call(this);
  console.log('Whoop whoop!  This is my framerate: ', this.fps);
}

// Set `Cartoon` to share `Tweenable`'s prototype
Cartoon.prototype = Tweenable.prototype;
var myCartoon = new Cartoon();

In this example, Cartoon is borrowing the prototype and constructor of Tweeneable to build the inheritance chain. This is a more advanced approach, but it will likely give you the most flexibility and minimize bizarre "shared prototype" bugs.

Using inheritance is awesome because any plugins or extensions that are present on the Tweenable() prototype are also available to myCartoon, and all instances of Cartoon (and Tweenable). You can define these inheritable functions by attaching them to the Tweenable.prototype Object. A full example of this:

// Add a new method to the `Tweenable` prototype
Tweenable.prototype.logMyProperties = function () {
  Tweenable.util.each(this, function (obj, prop) {
    console.log(prop + ': ', obj[prop]);
  });
}

// Define a constructor function
function Cartoon () {
  this.constructor.call(this);
  this.cartoonProp = "I am a property of Cartoon!  And not the Tweenable prototype!";
  console.log('Whoop whoop!  This is my framerate: ', this.fps);
}

Cartoon.prototype = Tweenable.prototype;

// Make a new instance of `cartoon`
var myCartoon = new Cartoon();

// Test the new prototype method
myCartoon.logMyProperties();

That's fun, but how do we hook functionality into Tweenable instances themselves?

Hooks

You can attach various hooks that get run at key points in a Tweenable instance's execution. The API:

/**
 * @param {String} hookName The `Tweenable` hook to attach the `hookFunction` to.
 * @param {Function} hookFunction The function to execute.
 */
tweenableInst.hookAdd( hookName, hookFunction )

You can attach as many functions as you please to any hook. Here's an example:

function limitX (state) {
  // Limit x to 300
  if (state.x > 300) {
    state.x = 300;
  }
}

var myTweenable = new Tweenable();

myTweenable.hookAdd('step', limitX);

This snippet will set the function limitX for hookFunction to be called every frame, after the values have been computed. You can also remove hooks. The API:

/**
 * @param {String} hookName The `Tweenable` hook to remove the `hookFunction` from.
 * @param {Function|undefined} hookFunction The function to remove.  If omitted, all functions attached to `hookName` are removed.
 */
tweenableInst.hookRemove( hookName, hookFunction )

Example, continuing from above:

myTweenable.hookRemove('step', stepHook);

The hooks you can currently attach functions to are:

  • step: Runs on every frame that a tween runs for. Hook handler function receives a tween's currentState for a parameter.

... And that's it. They're easy to add in, please make Github issue or pull request if you'd like more to be added.

Filters

Filters are used for transforming the properties of a tween at various points in a Tweenable instance's lifecycle. Filters differ from hooks because they get executed for all Tweenable instances globally. Additionally, they are meant to convert non-Number datatypes to Numbers so they can be tweened, and then back again. Just define a filter once, attach it to Tweenable.prototype, and all new instances of Tweenable will have access to it.

Here's an annotated example of a filter:

Tweenable.prototype.filter.doubler = {
  // Gets called when a tween is created.  `fromState` is the state that the tween starts at, and `toState` contains the target values.
  'tweenCreated': function tweenCreated (fromState, toState) {
    Tweenable.util.each(obj, function (fromState, prop) {
      // Double each initial state property value as soon as the tween is created.
      obj[prop] *= 2;
    });
  },

  // Gets called right before a tween state is calculated.
  // `currentState` is the current state of the tweened object, `fromState` is the state that the tween started at, and `toState` contains the target values.
  'beforeTween': function beforeTween (currentState, fromState, toState) {
    Tweenable.util.each(toState, function (obj, prop) {
      // Double each target property right before the tween formula is applied.
      obj[prop] *= 2;
    });
  },

  // Gets called right after a tween state is calculated.
  // `currentState` is the current state of the tweened object, `fromState` is the state that the tween started at, and `toState` contains the target values.
  'afterTween': function afterTween (currentState, fromState, toState) {
    Tweenable.util.each(toState, function (obj, prop) {
      // Return the target properties back to their pre-doubled values.
      obj[prop] /= 2;
    });
  }
}

Yes, having doubler filter is useless. A more practical use of filters is to add support for more data types. Remember, Tweenable only supports Numbers out of the box, but you can add support for strings, functions, or whatever else you might need. The px and color extensions work by filtering string values into numbers before each tween step, and then back again after the tween step.

Building Shifty

In the root directory, there is a file called build.sh. Just do this to build the project on the command line:

$: sh build.sh <version>

<version> is the label for the new build. Shifty uses SemVer.

The only requirement for building is curl and an internet connection (the script calls out to Google's Closure Compiler API). In English: If you are on a Mac, you're good to go. You can find a ready-to-use build of the project at builds/shifty.min.js. This build includes the core and all extensions. Feel free to customize the build for your own needs.

AMD and NodeJS

If an AMD loader (eg. RequireJS, Curl.js) is present on the page Shifty won't generate any globals, so to use it you must list "shifty" as a dependency.

define(['lib/shifty'], function(Tweenable){
  //shifty was loaded and is ready to be used
  var myAwesomeTweenable = new Tweenable();
  ...
});

Shifty can also be used on NodeJS:

var Tweenable = require('./shifty');
...

Shifty in Use

Shifty is in known to be use in the following projects/sites:

  • Rekapi. Shifty is a core component of Rekapi, a higher-level tool that allows developers to easily create and manage complex animations.
  • Galaxy Nexus Landing Page. Shifty was used to create animations bound to the browser's scroll event.
  • Morf.js, by Joe Lambert. Morf.js is a CSS3 Transition utility. It lets you define your own easing formulas, but also take advantage of hardware acceleration provided by Webkit browsers. Morf.js uses Shifty to calculate keyframe states.
  • html-timeline, by Thomas Reynolds. This project acts as a wrapper for Shifty that animates HTML elements as you scroll the page. Written in CoffeeScript!
Something went wrong with that request. Please try again.