diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..56bef8c --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +examples/ \ No newline at end of file diff --git a/README.md b/README.md index f6cf224..9e1c2b5 100644 --- a/README.md +++ b/README.md @@ -1,121 +1,194 @@ -# Storyline.js - Multi-purpose sequencer +# Storyline.js - JavaScript Animation Sequencer **Storyline.js** is a library to help define a storyboard using natural language. -This is the refined and polished version of the sytem created for [BEYOND](http://b-e-y-o-n-d.com/) and [cru·ci·form](http://www.clicktorelease.com/code/cruciform/). +[Check all the demos](http://jordandelcros.github.io/Storyline.js/) -Check out the example to see it in action: [Storyline.js with CSS 2D transforms](http://www.clicktorelease.com/tools/storylinejs/). +### How to? -[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/mQVU3Lb0D-w/0.jpg)](http://www.youtube.com/watch?v=mQVU3Lb0D-w) +The Storyline class only come with two options : + - storyboard: a simple object containing all keyframes. + - timescale: (optional) multiply all key's time with it. -#### Using Storyline.js #### - -There's two parts: Storyline.js is the parser and player, and then a storyboard source object that defines the story. A storyline source has this format: +```javascript + var storyline = new Storyline(storyboard [, timescale]); +``` -```json -{ - "value1": [ - "0 cut to 10", - "2 linear to 3" - ], - - "value2": [ - "0 cut to 0", - "4 ease to 1", - "6 ease to 0" - ] -} +#### Storyboard hierarchy + + - name: the name of the animation. + - time: the begining of the current value to the next one in milliseconds. + - easing: the easing to use. + - value: the value(s) to animate (multiple values are between parenthesis). + +```javascript + { + {name}: [ + "{time} {easing} to {value}", + "{time} {easing} to {value}", + "{time} {easing} to {value}" + ], + {name}: [ + "{time} {easing} to ({value}, {value}, {value})", + "{time} {easing} to ({value}, {value}, {value})" + ] + } ``` -This source object is a map of keys (each key is a value that you will be using in your code *x*, *angle*, *power*, etc.), and each key contains an array of entries. Each entry defines a point in time, and a storyline action, and has the following syntax: +#### Timing +Each key times are in milliseconds. + +```javascript + var storyline = new Storyline({ + key1: [ + "0 cut to 0", + "500 linear to 1", + "1000 linear to 2" + ] + }); ``` -{time in seconds} {action to perform} {value of action} + +You may need to create animations with a limited duration, you can use the `timescale` option to do that. +Each time will be multiplied by the `timescale`. + +```javascript + + var duration = 5000; // timescale of 5s + + var storyline = new Storyline({ + key1: [ + "0 cut to 0", + "0.3 linear to 3", + "1 linear to 4" + ] + }, duration); ``` -The actions are: +#### Easing + +There is already some basic easings: + - cut: immediately switch to the value if the time is upper or equal to the key. + - linear: linearly interpolate to the value. + - easeIn: ease in from the previous value to the key value. + - easeOut: ease out from the previous value to the key value. + - easeInOut: ease in and out from the previous value to the key value. + - easeInElastic: ease in elastic from the previous value to the key value. + - easeOutElastic: ease out elastic from the previous value to the key value. + - easeInOutElastic: ease in and out elastic from the previous value to the key value. + - easeInBounce: ease in bounce from the previous value to the key value. + - easeOutBounce: ease out bounce from the previous value to the key value. + - easeInOutBounce: ease in and out bounce from the previous value to the key value. + - quadratic(from,c,to): get value along a qaudratic bezier curve (see [stackoverflow explainations](http://stackoverflow.com/questions/5634460/quadratic-bezier-curve-calculate-point)). + - cubic(from,cx,cy,to): get value along a cubic bezier curve (see [stackoverflow explainations](http://stackoverflow.com/questions/5634460/quadratic-bezier-curve-calculate-point)). -- *cut to* instanteously changes to {value} -- *linear to* will linearly interpolate from the last defined value to {value} -- *ease to* will ease in-out from the last defined value to {value} +But you can also register your own easings: -#### Minimal example #### +```javascript + Storyline.registerEasing(customEasingName, function( elapsed, duration, options ){ -Include Storyline.js + return (elapsed / duration); // Linear easing -```html - + }); ``` -Create a storyline from a structured storyboard source. By calling storyline.get you can get the updated value: + - elpsed: normalized elpased time (between 0 and 1). + - duration: normalized duration (always 1...) + - options: array of values, only if the easing take options (parenthesis with parameters). -```js -var storyline = STORYLINE.parseStoryline( { +#### Type - "value1": [ - "0 cut to 0", - "5 ease to 1", - "10 ease to 0" - ] - -} ); +You can animate one or many values in each keys but you can also use types: + - int: only returns integers. + - bool: return true if the value is upper or equal to 1, else return false. + - vec2: return the values with `.x` and `.y` getters. + - vec3: return the values with `.x`, `.y` and `.z` getters. + - color: return the values with `.r`, `.g`, `.b`, `hex` and `hexString` getters. -function update() { - - requestAnimationFrame( update ); - console.log( storyline.get( 'value1', ( Date.now() / 1000 ) % 10 ) ); - -} +But you can also register your own types: -update(); -``` +`Storyline.registerType(name, getter, setter)` + +```javascript + Storyline.registerType("invert", function( options, originOptions ){ + + for( var option = 0, length = options.length; option < length; option++ ){ + + options[option] *= -1; + + }; -#### External storyboard example #### + return options; -Simply export the storyline into its own file, and include it like a normal script. + }, function( options, originString ){ -```js -var storyline = STORYLINE.parseStoryline( { + return options; - "value1": [ + }); + +``` + +### Examples + +#### Simple + +```javascript + var storyline = new Storyline({ + key1: [ "0 cut to 0", - "5 ease to 1", - "10 ease to 0" + "500 easeIn to 360", + "1000 linear to 180" + ], + key2: [ + "500 cut to 180", + "1000 lienar to 0" ] - -} ); -``` + }); -Or load the content with AJAX and parse it when it's loaded: + function update( now ){ -```js -var oReq = new XMLHttpRequest(); -oReq.onload = function() { - storyline = STORYLINE.parseStoryline( this.responseText ); - /* ready to use */ -}; -oReq.open( 'get', 'storyboard.json', true); -oReq.send(); -``` + window.requestAnimationFrame(update); -### Status #### + var key1 = storyline.get("key1", now); + var key2 = storyline.get("key2", now); -This is the first release. Next steps are to add syntax to control the easing functions, probably something like: + }; -``` -{time} ease to {value} { [ set of easing control points] } + window.requestAnimationFrame(update); ``` -Also, support specific functions to simplify animations: +#### With fixed duration -``` -{time} {wiggle|shake} {extent} +```javascript + var storyline = new Storyline({ + key1: [ + "0 cut to 0", + "0.5 easeIn to 360", + "1 linear to 180" + ], + key2: [ + "0.5 cut to 180", + "1 lienar to 0" + ] + }, 1000); + + function update( now ){ + + window.requestAnimationFrame(update); + + var key1 = storyline.get("key1", now); + var key2 = storyline.get("key2", now); + + }; + + window.requestAnimationFrame(update); ``` -As always: forks, pull requests and code critiques are welcome! +[Check all the demos/examples](http://jordandelcros.github.io/Storyline.js/) -#### License #### +#### License MIT licensed -Copyright (C) 2015 Jaume Sanchez Elias, http://www.clicktorelease.com \ No newline at end of file +Original idea from [Jaume Sanchez Elias](http://www.clicktorelease.com) +Entirely rewrited by [Jordan Delcros](http://www.jordan-delcros.com) \ No newline at end of file diff --git a/examples/index.html b/examples/index.html index 5eebcdc..170c2c8 100644 --- a/examples/index.html +++ b/examples/index.html @@ -1,153 +1,270 @@ - - - -
This is a demo of a storyboard that defines values in time that are applied to the CSS 2D transform of a div.
- - - - -Try changing the values. You can use:
-This is very minimal. Check your console!
- - - - - - - - diff --git a/package.json b/package.json new file mode 100644 index 0000000..2781b9c --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "storyline.js", + "version": "1.4.1", + "description": "javascript animation sequencer", + "main": "storyline.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/JordanDelcros/Storyline.js.git" + }, + "keywords": [ + "javascript", + "animation", + "sequencer", + "scenario", + "story", + "frame" + ], + "author": "Jordan Delcros", + "license": "MIT", + "bugs": { + "url": "https://github.com/JordanDelcros/Storyline.js/issues" + }, + "homepage": "https://github.com/JordanDelcros/Storyline.js#readme" +} diff --git a/src/storyline.js b/src/storyline.js deleted file mode 100644 index 35ad541..0000000 --- a/src/storyline.js +++ /dev/null @@ -1,167 +0,0 @@ -( function() { - -var ACTIONS = { - CUT: 0, - EASE: 1, - LINEAR: 2 -} - -function Event() { - this.start = null; - this.end = null; - this.action = null; - this.from = 0; - this.to = 0; - this.duration = 0; -} - -function Storyline() { - - this.points = {}; - - this.get = function( id, t ) { - return averageData( this.points, t, id ); - } - -} - -function parseStoryline( story ) { - - var s = new Storyline(); - - for( var v in story ) { - if( story.hasOwnProperty( v ) ) { - - var storyboard = []; - - story[ v ].forEach( function( e ) { - var start = e.match( /([^\s]+)/ ); - var event = new Event(); - if( e.indexOf( 'cut to' ) != -1 ) { - event.start = parseFloat( start[ 1 ] ); - event.action = ACTIONS.CUT; - var v = e.match( /[^\s]+ cut to ([^\s]+)/ ); - event.from = parseFloat( v[ 1 ] ); - event.to = event.from; - event.end = event.start; - } - if( e.indexOf( 'ease to' ) != -1 ) { - event.end = parseFloat( start[ 1 ] ); - event.action = ACTIONS.EASE; - event.from = 0; - var v = e.match( /[^\s]+ ease to ([^\s]+)/ ); - event.to = parseFloat( v[ 1 ] ); - } - if( e.indexOf( 'linear to' ) != -1 ) { - event.end = parseFloat( start[ 1 ] ); - event.action = ACTIONS.LINEAR; - event.from = 0; - var v = e.match( /[^\s]+ linear to ([^\s]+)/ ); - event.to = parseFloat( v[ 1 ] ); - } - storyboard.push( event ); - } ); - - storyboard.forEach( function( e, i ) { - if( e.action === ACTIONS.EASE || e.action == ACTIONS.LINEAR ) { - e.start = storyboard[ i - 1 ].end; - e.from = storyboard[ i - 1 ].to; - } - e.duration = e.end - e.start; - } ); - - storyboard.forEach( function( e, i ) { - if( e.action === ACTIONS.CUT ) { - if( storyboard[ i + 1 ] ) { - e.end = storyboard[ i + 1 ].start; - } - } - } ); - - /*storyboard.forEach( function( e, i ) { - console.log( e.from + '(' + e.start + ')' + ' to ' + e.to + '(' + e.end + ') in ' + e.duration ); - } );*/ - - s.points[ v ] = storyboard; - - } - } - - return s; - -} - -function getPointInStoryline( storyline, t, value ) { - - if( !storyline[ value ] ) return null; - - for( var j = 0; j < storyline[ value ].length; j++ ) { - var e = storyline[ value ][ j ]; - if( e.start <= t && e.end > t ) { - return e; - } - } - - return null; - -} - -function averageData( story, t, value ) { - - if( !story[ value ] ) { - console.warn( value + ' not found on storyboard' ); - return; - } - - var p; - - if( t > story[ value ][ story[ value ].length - 1 ].end ) { - p = story[ value ][ story[ value ].length - 1 ]; - } else { - p = getPointInStoryline( story, t, value ); - } - - if( !p ) return null; - - if( p.action === ACTIONS.CUT ) { - return p.from; - } - - if( p.action === ACTIONS.EASE ) { - - var et = ( t - p.start ) / p.duration; - et = Math.max( Math.min( et, 1 ), 0 ); - var easing; - if ( ( et *= 2 ) < 1 ) easing = 0.5 * et * et; - else easing = - 0.5 * ( --et * ( et - 2 ) - 1 ); - - var v = p.from + ( easing * ( p.to - p.from ) ); - - return v; - - } - - if( p.action === ACTIONS.LINEAR ) { - - var et = ( t - p.start ) / p.duration; - et = Math.max( Math.min( et, 1 ), 0 ); - var v = p.from + ( et * ( p.to - p.from ) ); - - return v; - - } - -} - -function setValue( original, value ) { - - if( value !== null ) return value; - return original - -} - -window.STORYLINE = { - parseStoryline: parseStoryline -} - -} )(); diff --git a/storyline.js b/storyline.js new file mode 100644 index 0000000..a04dba8 --- /dev/null +++ b/storyline.js @@ -0,0 +1,546 @@ +(function( self ){ + + var KEY = { + TIME: 0, + EASING: 1, + OPTION: 2, + VALUE: 3, + TYPE: 4 + }; + + var types = new Array(); + var easings = new Array(); + + var Storyline = function( story, timeScale ){ + + return new Storyline.methods.initialize(story, timeScale); + + }; + + Storyline.methods = Storyline.prototype = { + constructor: Storyline, + initialize: function( story, timeScale ){ + + this.storyboard = new Object(); + + this.timeScale = (parseFloat(timeScale) || 1); + + this.duration = 0; + + for( var key in story ){ + + for( var step in story[key] ){ + + var time = parseFloat(story[key][step].match(/^\s*(\-\s*)?([0-9]+\.?[0-9]*)/g)[0]); + + var easing = story[key][step].match(/([a-z]+)(?:\((.*)\))?/i); + var easingMode = Storyline.getEasing(easing[1]); + var easingOptions = (easing[2] != undefined ? easing[2].split(/\,/g) : null); + + var type = Storyline.getType((story[key][step].match(/([a-z0-9]+)\([^\)]+\)$/) || "")[1]); + var givenValue = story[key][step].match(/(?:(\-?\s*[0-9]+\.?[0-9]*)|\(([^\)]+)\))$/g)[0]; + var extractedValue = givenValue.match(/(\-?\s*(?:(?:0x)[0-9A-F]+|[0-9]+)\.?[0-9]*)/gi); + + var values = null; + + if( type != undefined && types[type] != undefined && types[type][2] != undefined ){ + + values = types[type][2](extractedValue, givenValue); + + } + else { + + values = extractedValue.map(function( number ){ + + return parseFloat(number); + + }); + + }; + + this.set(key, time, easingMode, easingOptions, values, type); + + }; + + }; + + return this; + + }, + set: function( key, time, easing, options, values, type ){ + + time *= this.timeScale; + + this.duration = Math.max(this.duration, time); + + if( this.storyboard[key] == undefined ){ + + this.storyboard[key] = new Array(); + + }; + + this.storyboard[key].push([time, easing, options, values, type]); + + this.storyboard[key].sort(function( before, after ){ + + return before[0] - after[0]; + + }); + + return this; + + }, + get: function( key, now ){ + + if( this.storyboard[key] != undefined ){ + + var type = null; + var values = new Array(); + + for( var stepMax = this.storyboard[key].length - 1, step = stepMax; step >= 0; step-- ){ + + type = (types[this.storyboard[key][step][KEY.TYPE]] || null); + + if( this.storyboard[key][step][KEY.TIME] <= now ){ + + var from = this.storyboard[key][step]; + var to = this.storyboard[key][Math.min(step + 1, stepMax)]; + + for( var valueIndex = 0, length = to[KEY.VALUE].length; valueIndex < length; valueIndex++ ){ + + var difference = to[KEY.VALUE][valueIndex] - from[KEY.VALUE][valueIndex]; + var duration = to[KEY.TIME] - from[KEY.TIME]; + + var elapsed = Math.min((((now - from[KEY.TIME]) / duration) || 0), 1); + + values[valueIndex] = from[KEY.VALUE][valueIndex] + (easings[to[KEY.EASING]][1](elapsed, 1, to[KEY.OPTION]) * difference); + + }; + + break; + + } + else if( step == 0 ){ + + values = this.storyboard[key][step][KEY.VALUE].slice(0); + + break; + + }; + + }; + + if( type != null ){ + + values = type[1](values, this.storyboard[key][step][KEY.VALUE]); + + }; + + return (values.length == 1 ? values[0] : values); + + }; + + return null; + + } + }; + + Storyline.methods.initialize.prototype = Storyline.methods; + + Storyline.registerType = function( name, getFunction, setFunction ){ + + types.push([name, getFunction, setFunction]); + + }; + + Storyline.getType = function( name ){ + + for( var type = 0, length = types.length; type < length; type++ ){ + + if( name == types[type][0] ){ + + return type; + + }; + + }; + + return null; + + }; + + Storyline.registerType("int", function( options, originString ){ + + for( var option = 0, length = options.length; option < length; option++ ){ + + options[option] = parseInt(options[option]); + + }; + + return options; + + }); + + Storyline.registerType("bool", function( options, originString ){ + + for( var option = 0, length = options.length; option < length; option++ ){ + + options[option] = (parseInt(options[option]) ? true : false); + + }; + + return options; + + }); + + Storyline.registerType("vec2", function( options, originString ){ + + options.x = options[0]; + options.y = options[1]; + + return options; + + }); + + Storyline.registerType("vec3", function( options, originString ){ + + options.x = options[0]; + options.y = options[1]; + options.z = options[2]; + + return options; + + }); + + Storyline.registerType("color", function( options, originString ){ + + options.r = options[0]; + options.g = options[1]; + options.b = options[2]; + options.a = options[3]; + + options.hex = (options[0] * 255) << 16 ^ (options[1] * 255) << 8 ^ (options[2] * 255) << 0; + options.hexString = "#" + ("000000" + options.hex.toString(16)).slice(-6); + + return options; + + }, function( options, originOptions ){ + + if( /^0x[0-9A-F]+/i.test(options[0]) == true ){ + + var hexadecimal = parseInt(options[0]); + + options[0] = ((hexadecimal >> 16 & 255) / 255); + options[1] = ((hexadecimal >> 8 & 255) / 255); + options[2] = ((hexadecimal & 255) / 255); + options[3] = 1; + + } + else if( options.length == 3 ){ + + options[3] = 1; + + }; + + return options.map(function( number ){ + + number = parseFloat(number); + + if( number > 1 ){ + + number /= 255; + + }; + + return number; + + }); + + }); + + Storyline.registerEasing = function( name, easingFunction ){ + + easings.push([name, easingFunction]); + + }; + + Storyline.getEasing = function( name ){ + + for( var easing = 0, length = easings.length; easing < length; easing++ ){ + + if( name == easings[easing][0] ){ + + return easing; + + }; + + }; + + return null; + + }; + + Storyline.registerEasing("cut", function( elapsed, duration, options ){ + + return (elapsed < 1 ? 0 : 1); + + }); + + Storyline.registerEasing("linear", function( elapsed, duration, options ){ + + return (elapsed / duration); + + }); + + Storyline.registerEasing("easeIn", function( elapsed, duration, options ){ + + return elapsed * elapsed * elapsed; + + }); + + Storyline.registerEasing("easeOut", function( elapsed, duration, options ){ + + return (elapsed -= 1) * elapsed * elapsed + 1; + + }); + + Storyline.registerEasing("easeInOut", function( elapsed, duration, options ){ + + if( (elapsed /= duration / 2) < 1 ){ + + return 0.5 * elapsed * elapsed * elapsed; + + } + else { + + return 0.5 * ((elapsed -= 2) * elapsed * elapsed + 2); + + }; + + }); + + Storyline.registerEasing("easeInElastic", function( elapsed, duration, options ){ + + if( elapsed == 0 ){ + + return 0; + + } + else if( (elapsed /= duration) == 1 ){ + + return 1; + + } + else { + + return -(1 * Math.pow(2, 10 * (elapsed -= 1)) * Math.sin((elapsed * duration - ((duration * 0.3) / (2 * Math.PI) * Math.asin(1))) * (2 * Math.PI) / (duration * 0.3))); + + }; + + }); + + Storyline.registerEasing("easeOutElastic", function( elapsed, duration, options ){ + + if( elapsed == 0 ){ + + return 0; + + } + else if( (elapsed /= duration) == 1 ){ + + return 1; + + } + else { + + return Math.pow(2, -10 * elapsed) * Math.sin((elapsed * duration - ((duration * 0.3) / (2 * Math.PI) * Math.asin(1))) * (2 * Math.PI) / (duration * 0.3)) + 1; + + }; + + }); + + Storyline.registerEasing("easeInOutElastic", function( elapsed, duration, options ){ + + if( elapsed === 0){ + + return 0; + + } + else if( (elapsed /= duration / 2) === 2 ){ + + return 1; + + } + else { + + var progress = (duration * (0.3 * 1.5)); + var speed = ((duration * (0.3 * 1.5)) / (2 * Math.PI) * Math.asin(1)); + + if( elapsed < 1 ){ + + return -0.5 * (1 * Math.pow(2, 10 * (elapsed -= 1)) * Math.sin((elapsed * duration - ((duration * (0.3 * 1.5)) / (2 * Math.PI) * Math.asin(1))) * (2 * Math.PI) / (duration * (0.3 * 1.5)))); + + } + else { + + return Math.pow(2, -10 * (elapsed -= 1)) * Math.sin((elapsed * duration - ((duration * (0.3 * 1.5)) / (2 * Math.PI) * Math.asin(1))) * (2 * Math.PI) / (duration * (0.3 * 1.5))) * 0.5 + 1; + + }; + + }; + + }); + + Storyline.registerEasing("easeInBounce", function( elapsed, duration, options ){ + + elapsed = (duration - elapsed); + + if( (elapsed /= duration) < (1 / 2.75) ){ + + return 1 - (1 * (7.5625 * elapsed * elapsed)); + + } + else if( elapsed < (2 / 2.75) ){ + + return 1 - (1 * (7.5625 * (elapsed -= (1.5 / 2.75)) * elapsed + 0.75)); + + } + else if( elapsed < (2.5 / 2.75) ){ + + return 1 - (1 * (7.5625 * (elapsed -= (2.25 / 2.75)) * elapsed + 0.9375)); + + } + else { + + return 1 - (1 * (7.5625 * (elapsed -= (2.625 / 2.75)) * elapsed + 0.984375)); + + }; + + }); + + Storyline.registerEasing("easeOutBounce", function( elapsed, duration, options ){ + + if( (elapsed /= duration) < (1 / 2.75) ){ + + return (1 * (7.5625 * elapsed * elapsed)); + + } + else if( elapsed < (2 / 2.75) ){ + + return (1 * (7.5625 * (elapsed -= (1.5 / 2.75)) * elapsed + 0.75)); + + } + else if( elapsed < (2.5 / 2.75) ){ + + return (1 * (7.5625 * (elapsed -= (2.25 / 2.75)) * elapsed + 0.9375)); + + } + else { + + return (1 * (7.5625 * (elapsed -= (2.625 / 2.75)) * elapsed + 0.984375)); + + }; + + }); + + Storyline.registerEasing("easeInOutBounce", function( elapsed, duration, options ){ + + if( elapsed < (duration / 2) ){ + + elapsed = (duration - (elapsed * 2)); + + if( (elapsed /= duration) < (1 / 2.75) ){ + + return ((1 - (1 * (7.5625 * elapsed * elapsed))) * 0.5); + + } + else if( elapsed < (2 / 2.75) ){ + + return ((1 - (1 * (7.5625 * (elapsed -= (1.5 / 2.75)) * elapsed + 0.75))) * 0.5); + + } + else if( elapsed < (2.5 / 2.75) ){ + + return ((1 - (1 * (7.5625 * (elapsed -= (2.25 / 2.75)) * elapsed + 0.9375))) * 0.5); + + } + else { + + return ((1 - (1 * (7.5625 * (elapsed -= (2.625 / 2.75)) * elapsed + 0.984375))) * 0.5); + + }; + + } + else { + + elapsed = ((elapsed * 2) - duration); + + if( (elapsed /= duration) < (1 / 2.75) ){ + + return (1 * (7.5625 * elapsed * elapsed)) * 0.5 + 1 * 0.5; + + } + else if( elapsed < (2 / 2.75) ){ + + return (1 * (7.5625 * (elapsed -= (1.5 / 2.75)) * elapsed + 0.75)) * 0.5 + 1 * 0.5; + + } + else if( elapsed < (2.5 / 2.75) ){ + + return (1 * (7.5625 * (elapsed -= (2.25 / 2.75)) * elapsed + 0.9375)) * 0.5 + 1 * 0.5; + + } + else { + + return (1 * (7.5625 * (elapsed -= (2.625 / 2.75)) * elapsed + 0.984375)) * 0.5 + 1 * 0.5; + + }; + + }; + + }); + + Storyline.registerEasing("quadratic", function( elapsed, duration, options ){ + + var invertedElapsed = 1 - elapsed; + var point1 = parseFloat(options[0]); + var point2 = parseFloat(options[1]); + var point3 = parseFloat(options[2]); + + return invertedElapsed * invertedElapsed * point1 + 2 * invertedElapsed * elapsed * point2 + elapsed * elapsed * point3; + + }); + + Storyline.registerEasing("cubic", function( elapsed, duration, options ){ + + var invertedElapsed = 1 - elapsed; + var point1 = parseFloat(options[0]); + var point2 = parseFloat(options[1]); + var point3 = parseFloat(options[2]); + var point4 = parseFloat(options[3]); + + return invertedElapsed * invertedElapsed * invertedElapsed * point1 + 3 * invertedElapsed * invertedElapsed * elapsed * point2 + 3 * invertedElapsed * elapsed * elapsed * point3 + elapsed * elapsed * elapsed * point4; + + }); + + if( typeof define !== "undefined" && define instanceof Function && define.amd != undefined ){ + + define(function(){ + + return Storyline; + + }); + + } + else if( typeof module !== "undefined" && module.exports ){ + + module.exports = Storyline; + + } + else if( self != undefined ){ + + self.Storyline = Storyline; + + }; + +})(self || {}); \ No newline at end of file