├── .gitignore ├── .jshintrc ├── Makefile ├── README.md ├── build.json ├── examples.html ├── integration ├── ender.js ├── ender.min.js └── integration.html ├── morpheus.js ├── morpheus.min.js ├── package.json ├── src ├── copyright.js ├── easings.js ├── ender.js ├── morpheus.js └── rtltr.js └── tests └── tests.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": ["assert", "refute", "define", "self"] 3 | , "boss": true 4 | , "shadow": true 5 | , "trailing": true 6 | , "latedef": true 7 | , "forin": false 8 | , "curly": false 9 | , "debug": true 10 | , "devel": false 11 | , "evil": true 12 | , "regexp": false 13 | , "undef": true 14 | , "sub": true 15 | , "white": false 16 | , "asi": true 17 | , "laxbreak": true 18 | , "eqnull": true 19 | , "browser": true 20 | , "node": true 21 | , "laxcomma": true 22 | , "proto": true 23 | , "expr": true 24 | , "es5": true 25 | , "strict": false 26 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # npm install smoosh -g 2 | boosh: 3 | node_modules/smoosh/bin/smoosh make ./build.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | _ _ ____ ____ ___ _ _ ____ _ _ ____ 2 | |\/| | | |__/ |__] |__| |___ | | [__ 3 | | | |__| | \ | | | |___ |__| ___] 4 | ----- 5 | A Brilliant Animator. 6 | 7 | Morpheus lets you "tween anything" in parallel on multiple elements; from colors to integers of any unit (px, em, %, etc), with easing transitions and bezier curves, including CSS3 [transforms](http://www.w3.org/TR/css3-2d-transforms/) (roate, scale, skew, & translate) -- all in a single high-performant loop utilizing the CPU-friendly [requestAnimationFrame](http://webstuff.nfshost.com/anim-timing/Overview.html) standard. 8 | 9 | It looks like this: 10 | 11 | ``` js 12 | var anim = morpheus(elements, { 13 | // CSS 14 | left: -50 15 | , top: 100 16 | , width: '+=50' 17 | , height: '-=50px' 18 | , fontSize: '30px' 19 | , color: '#f00' 20 | , transform: 'rotate(30deg) scale(+=3)' 21 | , "background-color": '#f00' 22 | 23 | // API 24 | , duration: 500 25 | , easing: easings.easeOut 26 | , bezier: [[100, 200], [200, 100]] 27 | , complete: function () { 28 | console.log('done') 29 | } 30 | }) 31 | 32 | // stop an animation 33 | anim.stop() 34 | 35 | // jump to the end of an animation and run 'complete' callback 36 | anim.stop(true) 37 | ``` 38 | 39 | General Tweening 40 | ------ 41 | 42 | ``` js 43 | morpheus.tween(1000, 44 | function (position) { 45 | // do stuff with position... 46 | // like for example, use bezier curve points :) 47 | var xy = morpheus.bezier([[startX, startY], <[n control points]>, [endX, endY]], position) 48 | }, 49 | function () { 50 | console.log('done') 51 | }, 52 | easings.bounce, 53 | 100, // start 54 | 300 // end 55 | ) 56 | ``` 57 | 58 | API 59 | --- 60 | 61 | ``` js 62 | /** 63 | * morpheus: 64 | * @param element(s): HTMLElement(s) 65 | * @param options: mixed bag between CSS Style properties & animation options 66 | * - {n} CSS properties|values 67 | * - value can be strings, integers, 68 | * - or callback function that receives element to be animated. method must return value to be tweened 69 | * - relative animations start with += or -= followed by integer 70 | * - duration: time in ms - defaults to 1000(ms) 71 | * - easing: a transition method - defaults to an 'easeOut' algorithm 72 | * - complete: a callback method for when all elements have finished 73 | * - bezier: array of arrays containing x|y coordinates that define the bezier points. defaults to none 74 | * - this may also be a function that receives element to be animated. it must return a value 75 | * @return animation instance 76 | */ 77 | ``` 78 | 79 | See the live examples 80 | 81 | Language LTR - RTL support 82 | --------------- 83 | For those who run web services that support languages spanning from LTR to RTL, you can use the drop-in plugin filed called rtltr.js found in the src directory which will automatically mirror all animations without having to change your implementation. It's pretty rad. 84 | 85 | Browser support 86 | ----------- 87 | Grade A & C Browsers according to Yahoo's [Graded Browser Support](http://developer.yahoo.com/yui/articles/gbs/). CSS3 transforms are only supported in browsers that support the transform specification. 88 | 89 | Ender integration 90 | -------- 91 | Got [Ender](http://ender.jit.su)? No? Get it. 92 | 93 | $ npm install ender -g 94 | 95 | Add Morpheus to your existing Ender build 96 | 97 | $ ender add morpheus 98 | 99 | Write code like a boss: 100 | 101 | ``` js 102 | $('#content .boosh').animate({ 103 | left: 911, 104 | complete: function () { 105 | console.log('boosh') 106 | } 107 | }) 108 | ``` 109 | 110 | Usage Notes 111 | ----------- 112 | 113 |

Color

114 | If you're serious about *color animation*, there's a few precautions you'll need to take ahead of time. In order to morph *from* one color to another, you need to make sure the elements you're animating *have an inherited color style* to start with. Furthermore, those styles need to be represented in rgb, or hex, and not a named color (like red, or orange) -- that is unless you want to write code to map the [color conversion](http://www.w3schools.com/css/css_colornames.asp) yourself. 115 | 116 | Therefore, at minimum, you need to set a color before animating. 117 | 118 | ``` js 119 | element.style.color = '#ff0'; 120 | morpheus(element, { 121 | color: '#000' 122 | }) 123 | ``` 124 | 125 | With [Bonzo](https://github.com/ded/bonzo) installed in [Ender](http://ender.no.de). 126 | 127 | ``` js 128 | $('div.things').css('color', '#ff0').animate({ 129 | color: '#000' 130 | }) 131 | ``` 132 | 133 |

Units

134 | If you're considering animating by a CSS unit of measurement like em, pt, or %, like-wise to color animation, you must set the size ahead of time before animating: 135 | 136 | ``` js 137 | $('div.intro') 138 | .css({ 139 | fontSize: '2em' 140 | , width: '50%' 141 | }) 142 | .animate({ 143 | fontSize: '+=1.5em' 144 | , width: '100%' 145 | }) 146 | ``` 147 | 148 | You also get two other fancy fading hooks 149 | 150 | ``` js 151 | $('p').fadeIn(250, function () { 152 | console.log('complete') 153 | }) 154 | 155 | $('p').fadeOut(500, function () { 156 | console.log('complete') 157 | }) 158 | ``` 159 | 160 |

Transforms

161 | Transforms can be animated in browsers that support them (IE9, FF, Chrome, Safari, Opera). morpheus.transform provides a shortcut to the correct style property for the browser (webkitTransform, MozTransform, etc). Like animating on units or color, you must set the property ahead of time before animating: 162 | 163 | ``` js 164 | element.style[morpheus.transform] = 'rotate(30deg) scale(1)' 165 | morpheus(element, { 166 | transform: 'rotate(0deg) scale(+=3)' 167 | }) 168 | ``` 169 | 170 | AMD Support 171 | ---------- 172 | 173 | ``` js 174 | require('morpheus.js', function (morpheus) { 175 | morpheus(elements, config) 176 | }) 177 | 178 | or as usual with ender 179 | 180 | var morpheus = require('morpheus') 181 | 182 | ``` 183 | 184 | ## Developers 185 | 186 | If you're looking to contribute. Add your changes to `src/morpheus.js` Then run the following 187 | 188 | ``` sh 189 | npm install . 190 | make 191 | open tests/tests.html 192 | ``` 193 | 194 | Morpheus (c) Dustin Diaz 2011 - License MIT 195 | 196 | **Happy Morphing!** 197 | -------------------------------------------------------------------------------- /build.json: -------------------------------------------------------------------------------- 1 | { 2 | "JAVASCRIPT": { 3 | "DIST_DIR": "./" 4 | , "morpheus": [ 5 | "./src/copyright.js" 6 | , "./src/morpheus.js" 7 | ] 8 | } 9 | , "JSHINT_OPTS": { 10 | "boss": true 11 | , "forin": false 12 | , "curly": false 13 | , "debug": false 14 | , "devel": false 15 | , "evil": false 16 | , "regexp": false 17 | , "undef": false 18 | , "sub": true 19 | , "white": false 20 | , "indent": 2 21 | , "asi": true 22 | , "laxbreak": true 23 | , "shadow": true 24 | , "laxcomma": true 25 | } 26 | } -------------------------------------------------------------------------------- /examples.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Morpheus Examples 6 | 53 | 54 | 55 | 56 | 61 | 62 | 63 |
64 |

Morpheus Examples

65 | 66 |
67 |

General

68 |
69 | 70 | 82 |
83 | 84 |
85 |

Color

86 |
87 | 88 | 101 |
102 | 103 |
104 |

Motion + Color

105 |
106 | 107 | 124 |
125 | 126 |
127 |

Alternative units

128 |
hello world
129 | 130 | 143 |
144 | 145 |
146 |

Bezier Curves

147 |
148 | 149 | 164 |
165 | 166 |
167 |

Multiple elements

168 |
169 |
170 |
171 |
172 | 173 | 207 |
208 | 209 |
210 |

stopping animations

211 |
212 | 241 |
242 | 243 |
244 |

Transform

245 |
scale
246 |
rotate
247 |
skew
248 |
translate
249 | 250 | 251 | 279 |
280 | 281 |
282 | 283 | 284 | -------------------------------------------------------------------------------- /integration/ender.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ============================================================= 3 | * Ender: open module JavaScript framework (https://ender.no.de) 4 | * Build: ender build domready qwery bonzo bowser 5 | * ============================================================= 6 | */ 7 | 8 | /*! 9 | * Ender-JS: open module JavaScript framework (client-lib) 10 | * copyright Dustin Diaz & Jacob Thornton 2011 (@ded @fat) 11 | * https://ender.no.de 12 | * License MIT 13 | */ 14 | !function (context) { 15 | 16 | // a global object for node.js module compatiblity 17 | // ============================================ 18 | 19 | context['global'] = context; 20 | 21 | // Implements simple module system 22 | // losely based on CommonJS Modules spec v1.1.1 23 | // ============================================ 24 | 25 | var modules = {}; 26 | 27 | function require (identifier) { 28 | var module = modules[identifier] || window[identifier]; 29 | if (!module) throw new Error("Requested module '" + identifier + "' has not been defined."); 30 | return module; 31 | } 32 | 33 | function provide (name, what) { 34 | return modules[name] = what; 35 | } 36 | 37 | context['provide'] = provide; 38 | context['require'] = require; 39 | 40 | // Implements Ender's $ global access object 41 | // ========================================= 42 | 43 | function aug(o, o2) { 44 | for (var k in o2) { 45 | k != 'noConflict' && k != '_VERSION' && (o[k] = o2[k]); 46 | } 47 | return o; 48 | } 49 | 50 | function boosh(s, r, els) { 51 | // string || node || nodelist || window 52 | if (ender._select && (typeof s == 'string' || s.nodeName || s.length && 'item' in s || s == window)) { 53 | els = ender._select(s, r); 54 | els.selector = s; 55 | } else { 56 | els = isFinite(s.length) ? s : [s]; 57 | } 58 | return aug(els, boosh); 59 | } 60 | 61 | function ender(s, r) { 62 | return boosh(s, r); 63 | } 64 | 65 | aug(ender, { 66 | _VERSION: '0.2.5', 67 | ender: function (o, chain) { 68 | aug(chain ? boosh : ender, o); 69 | }, 70 | fn: context.$ && context.$.fn || {} // for easy compat to jQuery plugins 71 | }); 72 | 73 | aug(boosh, { 74 | forEach: function (fn, scope, i) { 75 | // opt out of native forEach so we can intentionally call our own scope 76 | // defaulting to the current item and be able to return self 77 | for (i = 0, l = this.length; i < l; ++i) { 78 | i in this && fn.call(scope || this[i], this[i], i, this); 79 | } 80 | // return self for chaining 81 | return this; 82 | }, 83 | $: ender // handy reference to self 84 | }); 85 | 86 | var old = context.$; 87 | ender.noConflict = function () { 88 | context.$ = old; 89 | return this; 90 | }; 91 | 92 | (typeof module !== 'undefined') && module.exports && (module.exports = ender); 93 | // use subscript notation as extern for Closure compilation 94 | context['ender'] = context['$'] = context['ender'] || ender; 95 | 96 | }(this); 97 | 98 | !function () { 99 | 100 | var module = { exports: {} }, exports = module.exports; 101 | 102 | /*! 103 | * Qwery - A Blazing Fast query selector engine 104 | * https://github.com/ded/qwery 105 | * copyright Dustin Diaz & Jacob Thornton 2011 106 | * MIT License 107 | */ 108 | 109 | !function (name, definition) { 110 | if (typeof define == 'function') define(definition) 111 | else if (typeof module != 'undefined') module.exports = definition() 112 | else this[name] = definition() 113 | }('qwery', function () { 114 | var context = this 115 | , doc = document 116 | , c, i, j, k, l, m, o, p, r, v 117 | , el, node, found, classes, item, items, token 118 | , html = doc.documentElement 119 | , id = /#([\w\-]+)/ 120 | , clas = /\.[\w\-]+/g 121 | , idOnly = /^#([\w\-]+$)/ 122 | , classOnly = /^\.([\w\-]+)$/ 123 | , tagOnly = /^([\w\-]+)$/ 124 | , tagAndOrClass = /^([\w]+)?\.([\w\-]+)$/ 125 | , normalizr = /\s*([\s\+\~>])\s*/g 126 | , splitters = /[\s\>\+\~]/ 127 | , splittersMore = /(?![\s\w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^'"]*\]|[\s\w\+\-]*\))/ 128 | , specialChars = /([.*+?\^=!:${}()|\[\]\/\\])/g 129 | , simple = /^([a-z0-9]+)?(?:([\.\#]+[\w\-\.#]+)?)/ 130 | , attr = /\[([\w\-]+)(?:([\|\^\$\*\~]?\=)['"]?([ \w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^]+)["']?)?\]/ 131 | , pseudo = /:([\w\-]+)(\(['"]?([\s\w\+\-]+)['"]?\))?/ 132 | , dividers = new RegExp('(' + splitters.source + ')' + splittersMore.source, 'g') 133 | , tokenizr = new RegExp(splitters.source + splittersMore.source) 134 | , chunker = new RegExp(simple.source + '(' + attr.source + ')?' + '(' + pseudo.source + ')?') 135 | , walker = { 136 | ' ': function (node) { 137 | return node && node !== html && node.parentNode 138 | } 139 | , '>': function (node, contestant) { 140 | return node && node.parentNode == contestant.parentNode && node.parentNode; 141 | } 142 | , '~': function (node) { 143 | return node && node.previousSibling; 144 | } 145 | , '+': function (node, contestant, p1, p2) { 146 | if (!node) { 147 | return false; 148 | } 149 | p1 = previous(node); 150 | p2 = previous(contestant); 151 | return p1 && p2 && p1 == p2 && p1; 152 | } 153 | } 154 | function cache() { 155 | this.c = {} 156 | } 157 | cache.prototype = { 158 | g: function (k) { 159 | return this.c[k] || undefined 160 | } 161 | , s: function (k, v) { 162 | this.c[k] = v 163 | return v 164 | } 165 | } 166 | 167 | var classCache = new cache() 168 | , cleanCache = new cache() 169 | , attrCache = new cache() 170 | , tokenCache = new cache() 171 | 172 | function flatten(ar) { 173 | r = [] 174 | for (i = 0, l = ar.length; i < l; i++) { 175 | if (arrayLike(ar[i])) { 176 | r = r.concat(ar[i]) 177 | } else { 178 | r.push(ar[i]) 179 | } 180 | } 181 | return r 182 | } 183 | 184 | function previous(n) { 185 | while (n = n.previousSibling) { 186 | if (n.nodeType == 1) { 187 | break; 188 | } 189 | } 190 | return n 191 | } 192 | 193 | function q(query) { 194 | return query.match(chunker) 195 | } 196 | 197 | // this next method expect at most these args 198 | // given => div.hello[title="world"]:foo('bar') 199 | 200 | // div.hello[title="world"]:foo('bar'), div, .hello, [title="world"], title, =, world, :foo('bar'), foo, ('bar'), bar] 201 | 202 | function interpret(whole, tag, idsAndClasses, wholeAttribute, attribute, qualifier, value, wholePseudo, pseudo, wholePseudoVal, pseudoVal) { 203 | var m, c, k; 204 | if (tag && this.tagName.toLowerCase() !== tag) { 205 | return false 206 | } 207 | if (idsAndClasses && (m = idsAndClasses.match(id)) && m[1] !== this.id) { 208 | return false 209 | } 210 | if (idsAndClasses && (classes = idsAndClasses.match(clas))) { 211 | for (i = classes.length; i--;) { 212 | c = classes[i].slice(1) 213 | if (!(classCache.g(c) || classCache.s(c, new RegExp('(^|\\s+)' + c + '(\\s+|$)'))).test(this.className)) { 214 | return false 215 | } 216 | } 217 | } 218 | if (pseudo && qwery.pseudos[pseudo] && !qwery.pseudos[pseudo](this, pseudoVal)) { 219 | return false 220 | } 221 | if (wholeAttribute && !value) { 222 | o = this.attributes 223 | for (k in o) { 224 | if (Object.prototype.hasOwnProperty.call(o, k) && (o[k].name || k) == attribute) { 225 | return this 226 | } 227 | } 228 | } 229 | if (wholeAttribute && !checkAttr(qualifier, this.getAttribute(attribute) || '', value)) { 230 | return false 231 | } 232 | return this 233 | } 234 | 235 | function clean(s) { 236 | return cleanCache.g(s) || cleanCache.s(s, s.replace(specialChars, '\\$1')) 237 | } 238 | 239 | function checkAttr(qualify, actual, val) { 240 | switch (qualify) { 241 | case '=': 242 | return actual == val 243 | case '^=': 244 | return actual.match(attrCache.g('^=' + val) || attrCache.s('^=' + val, new RegExp('^' + clean(val)))) 245 | case '$=': 246 | return actual.match(attrCache.g('$=' + val) || attrCache.s('$=' + val, new RegExp(clean(val) + '$'))) 247 | case '*=': 248 | return actual.match(attrCache.g(val) || attrCache.s(val, new RegExp(clean(val)))) 249 | case '~=': 250 | return actual.match(attrCache.g('~=' + val) || attrCache.s('~=' + val, new RegExp('(?:^|\\s+)' + clean(val) + '(?:\\s+|$)'))) 251 | case '|=': 252 | return actual.match(attrCache.g('|=' + val) || attrCache.s('|=' + val, new RegExp('^' + clean(val) + '(-|$)'))) 253 | } 254 | return 0 255 | } 256 | 257 | function _qwery(selector) { 258 | var r = [], ret = [], i, j = 0, k, l, m, p, token, tag, els, root, intr, item, children 259 | , tokens = tokenCache.g(selector) || tokenCache.s(selector, selector.split(tokenizr)) 260 | , dividedTokens = selector.match(dividers), dividedToken 261 | tokens = tokens.slice(0) // this makes a copy of the array so the cached original is not effected 262 | 263 | if (!tokens.length) return r 264 | 265 | token = tokens.pop() 266 | root = tokens.length && (m = tokens[tokens.length - 1].match(idOnly)) ? doc.getElementById(m[1]) : doc 267 | 268 | if (!root) return r 269 | 270 | intr = q(token) 271 | els = dividedTokens && /^[+~]$/.test(dividedTokens[dividedTokens.length - 1]) ? function (r) { 272 | while (root = root.nextSibling) { 273 | root.nodeType == 1 && (intr[1] ? intr[1] == root.tagName.toLowerCase() : 1) && r.push(root) 274 | } 275 | return r 276 | }([]) : 277 | root.getElementsByTagName(intr[1] || '*') 278 | for (i = 0, l = els.length; i < l; i++) if (item = interpret.apply(els[i], intr)) r[j++] = item 279 | if (!tokens.length) return r 280 | 281 | // loop through all descendent tokens 282 | for (j = 0, l = r.length, k = 0; j < l; j++) { 283 | p = r[j] 284 | // loop through each token backwards crawling up tree 285 | for (i = tokens.length; i--;) { 286 | // loop through parent nodes 287 | while (p = walker[dividedTokens[i]](p, r[j])) { 288 | if (found = interpret.apply(p, q(tokens[i]))) break; 289 | } 290 | } 291 | found && (ret[k++] = r[j]) 292 | } 293 | return ret 294 | } 295 | 296 | function isNode(el) { 297 | return (el && el.nodeType && (el.nodeType == 1 || el.nodeType == 9)) 298 | } 299 | 300 | function uniq(ar) { 301 | var a = [], i, j; 302 | label: 303 | for (i = 0; i < ar.length; i++) { 304 | for (j = 0; j < a.length; j++) { 305 | if (a[j] == ar[i]) { 306 | continue label; 307 | } 308 | } 309 | a[a.length] = ar[i] 310 | } 311 | return a 312 | } 313 | 314 | function arrayLike(o) { 315 | return (typeof o === 'object' && isFinite(o.length)) 316 | } 317 | 318 | function normalizeRoot(root) { 319 | if (!root) return doc 320 | if (typeof root == 'string') return qwery(root)[0] 321 | if (arrayLike(root)) return root[0] 322 | return root 323 | } 324 | 325 | function qwery(selector, _root) { 326 | var root = normalizeRoot(_root) 327 | 328 | if (!root || !selector) return [] 329 | if (selector === window || isNode(selector)) { 330 | return !_root || (selector !== window && isNode(root) && isAncestor(selector, root)) ? [selector] : [] 331 | } 332 | if (selector && arrayLike(selector)) return flatten(selector) 333 | if (m = selector.match(idOnly)) return (el = doc.getElementById(m[1])) ? [el] : [] 334 | if (m = selector.match(tagOnly)) return flatten(root.getElementsByTagName(m[1])) 335 | return select(selector, root) 336 | } 337 | 338 | var isAncestor = 'compareDocumentPosition' in html ? 339 | function (element, container) { 340 | return (container.compareDocumentPosition(element) & 16) == 16; 341 | } : 'contains' in html ? 342 | function (element, container) { 343 | container = container == doc || container == window ? html : container 344 | return container !== element && container.contains(element) 345 | } : 346 | function (element, container) { 347 | while (element = element.parentNode) if (element === container) return 1 348 | return 0 349 | }, 350 | 351 | supportsCSS3 = function () { 352 | if (!doc.querySelector || !doc.querySelectorAll) return false 353 | 354 | try { return (doc.querySelectorAll(':nth-of-type(1)').length > 0) } 355 | catch (e) { return false } 356 | }(), 357 | 358 | select = supportsCSS3 ? 359 | function (selector, root) { 360 | if (doc.getElementsByClassName && (m = selector.match(classOnly))) { 361 | return flatten((root).getElementsByClassName(m[1])); 362 | } 363 | return flatten((root).querySelectorAll(selector)) 364 | } : 365 | function (selector, root) { 366 | selector = selector.replace(normalizr, '$1') 367 | var result = [], element, collection, collections = [], i 368 | if (m = selector.match(tagAndOrClass)) { 369 | items = root.getElementsByTagName(m[1] || '*'); 370 | r = classCache.g(m[2]) || classCache.s(m[2], new RegExp('(^|\\s+)' + m[2] + '(\\s+|$)')); 371 | for (i = 0, l = items.length, j = 0; i < l; i++) { 372 | r.test(items[i].className) && (result[j++] = items[i]); 373 | } 374 | return result 375 | } 376 | for (i = 0, items = selector.split(','), l = items.length; i < l; i++) { 377 | collections[i] = _qwery(items[i]) 378 | } 379 | for (i = 0, l = collections.length; i < l && (collection = collections[i]); i++) { 380 | var ret = collection 381 | if (root !== doc) { 382 | ret = [] 383 | for (j = 0, m = collection.length; j < m && (element = collection[j]); j++) { 384 | // make sure element is a descendent of root 385 | isAncestor(element, root) && ret.push(element) 386 | } 387 | } 388 | result = result.concat(ret) 389 | } 390 | return uniq(result) 391 | } 392 | 393 | qwery.uniq = uniq 394 | qwery.pseudos = {} 395 | 396 | var old = context.qwery 397 | qwery.noConflict = function () { 398 | context.qwery = old 399 | return this 400 | } 401 | 402 | return qwery 403 | }) 404 | 405 | provide("qwery", module.exports); 406 | 407 | !function (doc, $) { 408 | var q = require('qwery') 409 | , table = 'table' 410 | , nodeMap = { 411 | thead: table 412 | , tbody: table 413 | , tfoot: table 414 | , tr: 'tbody' 415 | , th: 'tr' 416 | , td: 'tr' 417 | , fieldset: 'form' 418 | , option: 'select' 419 | } 420 | function create(node, root) { 421 | var tag = /^<([^\s>]+)/.exec(node)[1] 422 | , el = (root || doc).createElement(nodeMap[tag] || 'div'), els = [] 423 | el.innerHTML = node 424 | var nodes = el.childNodes 425 | el = el.firstChild 426 | els.push(el) 427 | while (el = el.nextSibling) (el.nodeType == 1) && els.push(el) 428 | return els 429 | } 430 | 431 | $._select = function (s, r) { 432 | return /^\s*]+)/.exec(node) 1090 | , el = doc.createElement(tag && tagMap[tag[1].toLowerCase()] || 'div'), els = [] 1091 | el.innerHTML = node 1092 | var nodes = el.childNodes 1093 | el = el.firstChild 1094 | els.push(el) 1095 | while (el = el.nextSibling) (el.nodeType == 1) && els.push(el) 1096 | return els 1097 | 1098 | }() : is(node) ? [node.cloneNode(true)] : [] 1099 | } 1100 | 1101 | bonzo.doc = function () { 1102 | var vp = this.viewport() 1103 | return { 1104 | width: Math.max(doc.body.scrollWidth, html.scrollWidth, vp.width) 1105 | , height: Math.max(doc.body.scrollHeight, html.scrollHeight, vp.height) 1106 | } 1107 | } 1108 | 1109 | bonzo.firstChild = function (el) { 1110 | for (var c = el.childNodes, i = 0, j = (c && c.length) || 0, e; i < j; i++) { 1111 | if (c[i].nodeType === 1) { 1112 | e = c[j = i] 1113 | } 1114 | } 1115 | return e 1116 | } 1117 | 1118 | bonzo.viewport = function () { 1119 | return { 1120 | width: ie ? html.clientWidth : self.innerWidth 1121 | , height: ie ? html.clientHeight : self.innerHeight 1122 | } 1123 | } 1124 | 1125 | bonzo.isAncestor = 'compareDocumentPosition' in html ? 1126 | function (container, element) { 1127 | return (container.compareDocumentPosition(element) & 16) == 16 1128 | } : 'contains' in html ? 1129 | function (container, element) { 1130 | return container !== element && container.contains(element); 1131 | } : 1132 | function (container, element) { 1133 | while (element = element[parentNode]) { 1134 | if (element === container) { 1135 | return true 1136 | } 1137 | } 1138 | return false 1139 | } 1140 | 1141 | var old = context.bonzo 1142 | bonzo.noConflict = function () { 1143 | context.bonzo = old 1144 | return this 1145 | } 1146 | 1147 | return bonzo 1148 | }) 1149 | 1150 | 1151 | provide("bonzo", module.exports); 1152 | 1153 | !function ($) { 1154 | 1155 | var b = require('bonzo') 1156 | b.setQueryEngine($) 1157 | $.ender(b) 1158 | $.ender(b(), true) 1159 | $.ender({ 1160 | create: function (node) { 1161 | return $(b.create(node)) 1162 | } 1163 | }) 1164 | 1165 | $.id = function (id) { 1166 | return $([document.getElementById(id)]) 1167 | } 1168 | 1169 | function indexOf(ar, val) { 1170 | for (var i = 0; i < ar.length; i++) { 1171 | if (ar[i] === val) { 1172 | return i 1173 | } 1174 | } 1175 | return -1 1176 | } 1177 | 1178 | function uniq(ar) { 1179 | var a = [], i, j 1180 | label: 1181 | for (i = 0; i < ar.length; i++) { 1182 | for (j = 0; j < a.length; j++) { 1183 | if (a[j] == ar[i]) { 1184 | continue label 1185 | } 1186 | } 1187 | a[a.length] = ar[i] 1188 | } 1189 | return a 1190 | } 1191 | 1192 | $.ender({ 1193 | parents: function (selector, closest) { 1194 | var collection = $(selector), j, k, p, r = [] 1195 | for (j = 0, k = this.length; j < k; j++) { 1196 | p = this[j] 1197 | while (p = p.parentNode) { 1198 | if (indexOf(collection, p) !== -1) { 1199 | r.push(p) 1200 | if (closest) break; 1201 | } 1202 | } 1203 | } 1204 | return $(uniq(r)) 1205 | }, 1206 | 1207 | closest: function (selector) { 1208 | return this.parents(selector, true) 1209 | }, 1210 | 1211 | first: function () { 1212 | return $(this[0]) 1213 | }, 1214 | 1215 | last: function () { 1216 | return $(this[this.length - 1]) 1217 | }, 1218 | 1219 | next: function () { 1220 | return $(b(this).next()) 1221 | }, 1222 | 1223 | previous: function () { 1224 | return $(b(this).previous()) 1225 | }, 1226 | 1227 | appendTo: function (t) { 1228 | return b(this.selector).appendTo(t, this) 1229 | }, 1230 | 1231 | prependTo: function (t) { 1232 | return b(this.selector).prependTo(t, this) 1233 | }, 1234 | 1235 | insertAfter: function (t) { 1236 | return b(this.selector).insertAfter(t, this) 1237 | }, 1238 | 1239 | insertBefore: function (t) { 1240 | return b(this.selector).insertBefore(t, this) 1241 | }, 1242 | 1243 | siblings: function () { 1244 | var i, l, p, r = [] 1245 | for (i = 0, l = this.length; i < l; i++) { 1246 | p = this[i] 1247 | while (p = p.previousSibling) { 1248 | p.nodeType == 1 && r.push(p) 1249 | } 1250 | p = this[i] 1251 | while (p = p.nextSibling) { 1252 | p.nodeType == 1 && r.push(p) 1253 | } 1254 | } 1255 | return $(r) 1256 | }, 1257 | 1258 | children: function () { 1259 | var i, el, r = [] 1260 | for (i = 0, l = this.length; i < l; i++) { 1261 | if (!(el = b.firstChild(this[i]))) { 1262 | continue; 1263 | } 1264 | r.push(el); 1265 | while (el = el.nextSibling) el.nodeType == 1 && r.push(el) 1266 | } 1267 | return $(uniq(r)) 1268 | }, 1269 | 1270 | height: function (v) { 1271 | return dimension(v, this, 'height') 1272 | }, 1273 | 1274 | width: function (v) { 1275 | return dimension(v, this, 'width') 1276 | } 1277 | }, true) 1278 | 1279 | function dimension(v, self, which) { 1280 | return v ? 1281 | self.css(which, v) : 1282 | function (r) { 1283 | r = parseInt(self.css(which), 10); 1284 | return isNaN(r) ? self[0]['offset' + which.replace(/^\w/, function (m) {return m.toUpperCase()})] : r 1285 | }() 1286 | } 1287 | 1288 | }(ender); 1289 | 1290 | 1291 | }(); 1292 | 1293 | !function () { 1294 | 1295 | var module = { exports: {} }, exports = module.exports; 1296 | 1297 | /*! 1298 | * Bowser - a browser detector 1299 | * copyright Dustin Diaz 2011 1300 | * https://github.com/ded/bowser 1301 | * MIT License 1302 | */ 1303 | /*! 1304 | * Bowser - a browser detector 1305 | * copyright Dustin Diaz 2011 1306 | * https://github.com/ded/bowser 1307 | * MIT License 1308 | */ 1309 | !function (context) { 1310 | /** 1311 | * navigator.userAgent => 1312 | * Chrome: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57 Safari/534.24" 1313 | * Opera: "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.7; U; en) Presto/2.7.62 Version/11.01" 1314 | * Safari: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1" 1315 | * IE: "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C)" 1316 | * Firefox: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0) Gecko/20100101 Firefox/4.0" 1317 | */ 1318 | 1319 | var ua = navigator.userAgent, 1320 | ie = /msie/i.test(ua), 1321 | chrome = /chrome/i.test(ua), 1322 | safari = /safari/i.test(ua) && !chrome, 1323 | opera = /opera/i.test(ua), 1324 | firefox = /firefox/i.test(ua), 1325 | gecko = /gecko\//i.test(ua); 1326 | 1327 | function detect() { 1328 | 1329 | if (ie) { 1330 | return { 1331 | msie: 1, 1332 | version: ua.match(/msie (\d+(\.\d+)?);/i)[1] 1333 | }; 1334 | } 1335 | if (chrome) { 1336 | return { 1337 | webkit: 1, 1338 | chrome: 1, 1339 | version: ua.match(/chrome\/(\d+(\.\d+)?)/i)[1] 1340 | }; 1341 | } 1342 | if (safari) { 1343 | return { 1344 | webkit: 1, 1345 | safari: 1, 1346 | version: ua.match(/version\/(\d+(\.\d+)?)/i)[1] 1347 | }; 1348 | } 1349 | if (opera) { 1350 | return { 1351 | opera: 1, 1352 | version: ua.match(/version\/(\d+(\.\d+)?)/i)[1] 1353 | }; 1354 | } 1355 | if (gecko) { 1356 | var o = { 1357 | gecko: 1, 1358 | version: ua.match(/firefox\/(\d+(\.\d+)?)/i)[1] 1359 | }; 1360 | if (firefox) { 1361 | o.firefox = 1; 1362 | } 1363 | return o; 1364 | } 1365 | 1366 | } 1367 | 1368 | var bowser = detect(); 1369 | 1370 | // Graded Browser Support 1371 | // http://developer.yahoo.com/yui/articles/gbs 1372 | if ((bowser.msie && bowser.version >= 6) || 1373 | (bowser.chrome && bowser.version >= 8) || 1374 | (bowser.firefox && bowser.version >= 3.6) || 1375 | (bowser.safari && bowser.version >= 5) || 1376 | (bowser.opera && bowser.version >= 9.5)) { 1377 | bowser.a = true; 1378 | } 1379 | 1380 | else if ((bowser.msie && bowser.version < 6) || 1381 | (bowser.chrome && bowser.version < 8) || 1382 | (bowser.firefox && bowser.version < 3.6) || 1383 | (bowser.safari && bowser.version < 5) || 1384 | (bowser.opera && bowser.version < 9.5)) { 1385 | bowser.c = true; 1386 | } else { 1387 | bowser.x = true; 1388 | } 1389 | 1390 | typeof module !== 'undefined' && module.exports ? 1391 | (module.exports.browser = bowser) : 1392 | (context.bowser = bowser); 1393 | 1394 | }(this); 1395 | 1396 | 1397 | provide("bowser", module.exports); 1398 | 1399 | $.ender(module.exports); 1400 | 1401 | }(); -------------------------------------------------------------------------------- /integration/ender.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ============================================================= 3 | * Ender: open module JavaScript framework (https://ender.no.de) 4 | * Build: ender build domready qwery bonzo bowser 5 | * ============================================================= 6 | */ 7 | 8 | 9 | /*! 10 | * Ender-JS: open module JavaScript framework (client-lib) 11 | * copyright Dustin Diaz & Jacob Thornton 2011 (@ded @fat) 12 | * https://ender.no.de 13 | * License MIT 14 | */ 15 | !function(a){function g(a,b){return f(a,b)}function f(a,b,c){g._select&&(typeof a=="string"||a.nodeName||a.length&&"item"in a||a==window)?(c=g._select(a,b),c.selector=a):c=isFinite(a.length)?a:[a];return e(c,f)}function e(a,b){for(var c in b)c!="noConflict"&&c!="_VERSION"&&(a[c]=b[c]);return a}function d(a,c){return b[a]=c}function c(a){var c=b[a]||window[a];if(!c)throw new Error("Requested module '"+a+"' has not been defined.");return c}a.global=a;var b={};a.provide=d,a.require=c,e(g,{_VERSION:"0.2.5",ender:function(a,b){e(b?f:g,a)},fn:a.$&&a.$.fn||{}}),e(f,{forEach:function(a,b,c){for(c=0,l=this.length;c])\s*/g,B=/[\s\>\+\~]/,C=/(?![\s\w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^'"]*\]|[\s\w\+\-]*\))/,D=/([.*+?\^=!:${}()|\[\]\/\\])/g,E=/^([a-z0-9]+)?(?:([\.\#]+[\w\-\.#]+)?)/,F=/\[([\w\-]+)(?:([\|\^\$\*\~]?\=)['"]?([ \w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^]+)["']?)?\]/,G=/:([\w\-]+)(\(['"]?([\s\w\+\-]+)['"]?\))?/,H=new RegExp("("+B.source+")"+C.source,"g"),I=new RegExp(B.source+C.source),J=new RegExp(E.source+"("+F.source+")?"+"("+G.source+")?"),K={" ":function(a){return a&&a!==t&&a.parentNode},">":function(a,b){return a&&a.parentNode==b.parentNode&&a.parentNode},"~":function(a){return a&&a.previousSibling},"+":function(a,b,c,d){if(!a)return!1;c=R(a),d=R(b);return c&&d&&c==d&&c}};L.prototype={g:function(a){return this.c[a]||undefined},s:function(a,b){this.c[a]=b;return b}};var M=new L,N=new L,O=new L,P=new L,ba="compareDocumentPosition"in t?function(a,b){return(b.compareDocumentPosition(a)&16)==16}:"contains"in t?function(a,c){c=c==b||c==window?t:c;return c!==a&&c.contains(a)}:function(a,b){while(a=a.parentNode)if(a===b)return 1;return 0},bb=function(){if(!b.querySelector||!b.querySelectorAll)return!1;try{return b.querySelectorAll(":nth-of-type(1)").length>0}catch(a){return!1}}(),bc=bb?function(a,c){return b.getElementsByClassName&&(h=a.match(x))?Q(c.getElementsByClassName(h[1])):Q(c.querySelectorAll(a))}:function(a,c){a=a.replace(A,"$1");var d=[],f,i,j=[],l;if(h=a.match(z)){r=c.getElementsByTagName(h[1]||"*"),k=M.g(h[2])||M.s(h[2],new RegExp("(^|\\s+)"+h[2]+"(\\s+|$)"));for(l=0,g=r.length,e=0;l]+)/.exec(b)[1],f=(c||a).createElement(e[d]||"div"),g=[];f.innerHTML=b;var h=f.childNodes;f=f.firstChild,g.push(f);while(f=f.nextSibling)f.nodeType==1&&g.push(f);return g}var c=require("qwery"),d="table",e={thead:d,tbody:d,tfoot:d,tr:"tbody",th:"tr",td:"tr",fieldset:"form",option:"select"};b._select=function(a,b){return/^\s*]+)/.exec(a),d=c.createElement(b&&k[b[1].toLowerCase()]||"div"),e=[];d.innerHTML=a;var f=d.childNodes;d=d.firstChild,e.push(d);while(d=d.nextSibling)d.nodeType==1&&e.push(d);return e}():A(a)?[a.cloneNode(!0)]:[]},N.doc=function(){var a=this.viewport();return{width:Math.max(c.body.scrollWidth,d.scrollWidth,a.width),height:Math.max(c.body.scrollHeight,d.scrollHeight,a.height)}},N.firstChild=function(a){for(var b=a.childNodes,c=0,d=b&&b.length||0,e;c=6||k.chrome&&k.version>=8||k.firefox&&k.version>=3.6||k.safari&&k.version>=5||k.opera&&k.version>=9.5?k.a=!0:k.msie&&k.version<6||k.chrome&&k.version<8||k.firefox&&k.version<3.6||k.safari&&k.version<5||k.opera&&k.version<9.5?k.c=!0:k.x=!0,typeof a!="undefined"&&a.exports?a.exports.browser=k:b.bowser=k}(this),provide("bowser",a.exports),$.ender(a.exports)}() -------------------------------------------------------------------------------- /integration/integration.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ender integration 6 | 66 | 67 | 68 | 69 | 70 | 128 | 129 | 130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | 144 |
mø®phéüs
145 | 146 | -------------------------------------------------------------------------------- /morpheus.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Morpheus - A Brilliant Animator 3 | * https://github.com/ded/morpheus - (c) Dustin Diaz 2011 4 | * License MIT 5 | */ 6 | !function (name, definition) { 7 | if (typeof define == 'function') define(definition) 8 | else if (typeof module != 'undefined') module.exports = definition() 9 | else this[name] = definition() 10 | }('morpheus', function () { 11 | 12 | var doc = document 13 | , win = window 14 | , perf = win.performance 15 | , perfNow = perf && (perf.now || perf.webkitNow || perf.msNow || perf.mozNow) 16 | , now = perfNow ? function () { return perfNow.call(perf) } : function () { return +new Date() } 17 | , fixTs = false // feature detected below 18 | , html = doc.documentElement 19 | , thousand = 1000 20 | , rgbOhex = /^rgb\(|#/ 21 | , relVal = /^([+\-])=([\d\.]+)/ 22 | , numUnit = /^(?:[\+\-]=?)?\d+(?:\.\d+)?(%|in|cm|mm|em|ex|pt|pc|px)$/ 23 | , rotate = /rotate\(((?:[+\-]=)?([\-\d\.]+))deg\)/ 24 | , scale = /scale\(((?:[+\-]=)?([\d\.]+))\)/ 25 | , skew = /skew\(((?:[+\-]=)?([\-\d\.]+))deg, ?((?:[+\-]=)?([\-\d\.]+))deg\)/ 26 | , translate = /translate\(((?:[+\-]=)?([\-\d\.]+))px, ?((?:[+\-]=)?([\-\d\.]+))px\)/ 27 | // these elements do not require 'px' 28 | , unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, transform: 1} 29 | 30 | // which property name does this browser use for transform 31 | var transform = function () { 32 | var styles = doc.createElement('a').style 33 | , props = ['webkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'Transform'] 34 | , i 35 | for (i = 0; i < props.length; i++) { 36 | if (props[i] in styles) return props[i] 37 | } 38 | }() 39 | 40 | // does this browser support the opacity property? 41 | var opasity = function () { 42 | return typeof doc.createElement('a').style.opacity !== 'undefined' 43 | }() 44 | 45 | // initial style is determined by the elements themselves 46 | var getStyle = doc.defaultView && doc.defaultView.getComputedStyle ? 47 | function (el, property) { 48 | property = property == 'transform' ? transform : property 49 | property = camelize(property) 50 | var value = null 51 | , computed = doc.defaultView.getComputedStyle(el, '') 52 | computed && (value = computed[property]) 53 | return el.style[property] || value 54 | } : html.currentStyle ? 55 | 56 | function (el, property) { 57 | property = camelize(property) 58 | 59 | if (property == 'opacity') { 60 | var val = 100 61 | try { 62 | val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity 63 | } catch (e1) { 64 | try { 65 | val = el.filters('alpha').opacity 66 | } catch (e2) {} 67 | } 68 | return val / 100 69 | } 70 | var value = el.currentStyle ? el.currentStyle[property] : null 71 | return el.style[property] || value 72 | } : 73 | function (el, property) { 74 | return el.style[camelize(property)] 75 | } 76 | 77 | var frame = function () { 78 | // native animation frames 79 | // http://webstuff.nfshost.com/anim-timing/Overview.html 80 | // http://dev.chromium.org/developers/design-documents/requestanimationframe-implementation 81 | return win.requestAnimationFrame || 82 | win.webkitRequestAnimationFrame || 83 | win.mozRequestAnimationFrame || 84 | win.msRequestAnimationFrame || 85 | win.oRequestAnimationFrame || 86 | function (callback) { 87 | win.setTimeout(function () { 88 | callback(+new Date()) 89 | }, 17) // when I was 17.. 90 | } 91 | }() 92 | 93 | frame(function(timestamp) { 94 | // feature-detect if rAF and now() are of the same scale (epoch or high-res), 95 | // if not, we have to do a timestamp fix on each frame 96 | fixTs = timestamp > 1e12 != now() > 1e12 97 | }) 98 | 99 | var children = [] 100 | 101 | function has(array, elem, i) { 102 | if (Array.prototype.indexOf) return array.indexOf(elem) 103 | for (i = 0; i < array.length; ++i) { 104 | if (array[i] === elem) return i 105 | } 106 | } 107 | 108 | function render(timestamp) { 109 | var i, count = children.length 110 | if (fixTs) timestamp = now() 111 | for (i = count; i--;) { 112 | children[i](timestamp) 113 | } 114 | children.length && frame(render) 115 | } 116 | 117 | function live(f) { 118 | if (children.push(f) === 1) frame(render) 119 | } 120 | 121 | function die(f) { 122 | var rest, index = has(children, f) 123 | if (index >= 0) { 124 | rest = children.slice(index + 1) 125 | children.length = index 126 | children = children.concat(rest) 127 | } 128 | } 129 | 130 | function parseTransform(style, base) { 131 | var values = {}, m 132 | if (m = style.match(rotate)) values.rotate = by(m[1], base ? base.rotate : null) 133 | if (m = style.match(scale)) values.scale = by(m[1], base ? base.scale : null) 134 | if (m = style.match(skew)) {values.skewx = by(m[1], base ? base.skewx : null); values.skewy = by(m[3], base ? base.skewy : null)} 135 | if (m = style.match(translate)) {values.translatex = by(m[1], base ? base.translatex : null); values.translatey = by(m[3], base ? base.translatey : null)} 136 | return values 137 | } 138 | 139 | function formatTransform(v) { 140 | var s = '' 141 | if ('rotate' in v) s += 'rotate(' + v.rotate + 'deg) ' 142 | if ('scale' in v) s += 'scale(' + v.scale + ') ' 143 | if ('translatex' in v) s += 'translate(' + v.translatex + 'px,' + v.translatey + 'px) ' 144 | if ('skewx' in v) s += 'skew(' + v.skewx + 'deg,' + v.skewy + 'deg)' 145 | return s 146 | } 147 | 148 | function rgb(r, g, b) { 149 | return '#' + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1) 150 | } 151 | 152 | // convert rgb and short hex to long hex 153 | function toHex(c) { 154 | var m = c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/) 155 | return (m ? rgb(m[1], m[2], m[3]) : c) 156 | .replace(/#(\w)(\w)(\w)$/, '#$1$1$2$2$3$3') // short skirt to long jacket 157 | } 158 | 159 | // change font-size => fontSize etc. 160 | function camelize(s) { 161 | return s.replace(/-(.)/g, function (m, m1) { 162 | return m1.toUpperCase() 163 | }) 164 | } 165 | 166 | // aren't we having it? 167 | function fun(f) { 168 | return typeof f == 'function' 169 | } 170 | 171 | function nativeTween(t) { 172 | // default to a pleasant-to-the-eye easeOut (like native animations) 173 | return Math.sin(t * Math.PI / 2) 174 | } 175 | 176 | /** 177 | * Core tween method that requests each frame 178 | * @param duration: time in milliseconds. defaults to 1000 179 | * @param fn: tween frame callback function receiving 'position' 180 | * @param done {optional}: complete callback function 181 | * @param ease {optional}: easing method. defaults to easeOut 182 | * @param from {optional}: integer to start from 183 | * @param to {optional}: integer to end at 184 | * @returns method to stop the animation 185 | */ 186 | function tween(duration, fn, done, ease, from, to) { 187 | ease = fun(ease) ? ease : morpheus.easings[ease] || nativeTween 188 | var time = duration || thousand 189 | , self = this 190 | , diff = to - from 191 | , start = now() 192 | , stop = 0 193 | , end = 0 194 | 195 | function run(t) { 196 | var delta = t - start 197 | if (delta > time || stop) { 198 | to = isFinite(to) ? to : 1 199 | stop ? end && fn(to) : fn(to) 200 | die(run) 201 | return done && done.apply(self) 202 | } 203 | // if you don't specify a 'to' you can use tween as a generic delta tweener 204 | // cool, eh? 205 | isFinite(to) ? 206 | fn((diff * ease(delta / time)) + from) : 207 | fn(ease(delta / time)) 208 | } 209 | 210 | live(run) 211 | 212 | return { 213 | stop: function (jump) { 214 | stop = 1 215 | end = jump // jump to end of animation? 216 | if (!jump) done = null // remove callback if not jumping to end 217 | } 218 | } 219 | } 220 | 221 | /** 222 | * generic bezier method for animating x|y coordinates 223 | * minimum of 2 points required (start and end). 224 | * first point start, last point end 225 | * additional control points are optional (but why else would you use this anyway ;) 226 | * @param points: array containing control points 227 | [[0, 0], [100, 200], [200, 100]] 228 | * @param pos: current be(tween) position represented as float 0 - 1 229 | * @return [x, y] 230 | */ 231 | function bezier(points, pos) { 232 | var n = points.length, r = [], i, j 233 | for (i = 0; i < n; ++i) { 234 | r[i] = [points[i][0], points[i][1]] 235 | } 236 | for (j = 1; j < n; ++j) { 237 | for (i = 0; i < n - j; ++i) { 238 | r[i][0] = (1 - pos) * r[i][0] + pos * r[parseInt(i + 1, 10)][0] 239 | r[i][1] = (1 - pos) * r[i][1] + pos * r[parseInt(i + 1, 10)][1] 240 | } 241 | } 242 | return [r[0][0], r[0][1]] 243 | } 244 | 245 | // this gets you the next hex in line according to a 'position' 246 | function nextColor(pos, start, finish) { 247 | var r = [], i, e, from, to 248 | for (i = 0; i < 6; i++) { 249 | from = Math.min(15, parseInt(start.charAt(i), 16)) 250 | to = Math.min(15, parseInt(finish.charAt(i), 16)) 251 | e = Math.floor((to - from) * pos + from) 252 | e = e > 15 ? 15 : e < 0 ? 0 : e 253 | r[i] = e.toString(16) 254 | } 255 | return '#' + r.join('') 256 | } 257 | 258 | // this retreives the frame value within a sequence 259 | function getTweenVal(pos, units, begin, end, k, i, v) { 260 | if (k == 'transform') { 261 | v = {} 262 | for (var t in begin[i][k]) { 263 | v[t] = (t in end[i][k]) ? Math.round(((end[i][k][t] - begin[i][k][t]) * pos + begin[i][k][t]) * thousand) / thousand : begin[i][k][t] 264 | } 265 | return v 266 | } else if (typeof begin[i][k] == 'string') { 267 | return nextColor(pos, begin[i][k], end[i][k]) 268 | } else { 269 | // round so we don't get crazy long floats 270 | v = Math.round(((end[i][k] - begin[i][k]) * pos + begin[i][k]) * thousand) / thousand 271 | // some css properties don't require a unit (like zIndex, lineHeight, opacity) 272 | if (!(k in unitless)) v += units[i][k] || 'px' 273 | return v 274 | } 275 | } 276 | 277 | // support for relative movement via '+=n' or '-=n' 278 | function by(val, start, m, r, i) { 279 | return (m = relVal.exec(val)) ? 280 | (i = parseFloat(m[2])) && (start + (m[1] == '+' ? 1 : -1) * i) : 281 | parseFloat(val) 282 | } 283 | 284 | /** 285 | * morpheus: 286 | * @param element(s): HTMLElement(s) 287 | * @param options: mixed bag between CSS Style properties & animation options 288 | * - {n} CSS properties|values 289 | * - value can be strings, integers, 290 | * - or callback function that receives element to be animated. method must return value to be tweened 291 | * - relative animations start with += or -= followed by integer 292 | * - duration: time in ms - defaults to 1000(ms) 293 | * - easing: a transition method - defaults to an 'easeOut' algorithm 294 | * - complete: a callback method for when all elements have finished 295 | * - bezier: array of arrays containing x|y coordinates that define the bezier points. defaults to none 296 | * - this may also be a function that receives element to be animated. it must return a value 297 | */ 298 | function morpheus(elements, options) { 299 | var els = elements ? (els = isFinite(elements.length) ? elements : [elements]) : [], i 300 | , complete = options.complete 301 | , duration = options.duration 302 | , ease = options.easing 303 | , points = options.bezier 304 | , begin = [] 305 | , end = [] 306 | , units = [] 307 | , bez = [] 308 | , originalLeft 309 | , originalTop 310 | 311 | if (points) { 312 | // remember the original values for top|left 313 | originalLeft = options.left; 314 | originalTop = options.top; 315 | delete options.right; 316 | delete options.bottom; 317 | delete options.left; 318 | delete options.top; 319 | } 320 | 321 | for (i = els.length; i--;) { 322 | 323 | // record beginning and end states to calculate positions 324 | begin[i] = {} 325 | end[i] = {} 326 | units[i] = {} 327 | 328 | // are we 'moving'? 329 | if (points) { 330 | 331 | var left = getStyle(els[i], 'left') 332 | , top = getStyle(els[i], 'top') 333 | , xy = [by(fun(originalLeft) ? originalLeft(els[i]) : originalLeft || 0, parseFloat(left)), 334 | by(fun(originalTop) ? originalTop(els[i]) : originalTop || 0, parseFloat(top))] 335 | 336 | bez[i] = fun(points) ? points(els[i], xy) : points 337 | bez[i].push(xy) 338 | bez[i].unshift([ 339 | parseInt(left, 10), 340 | parseInt(top, 10) 341 | ]) 342 | } 343 | 344 | for (var k in options) { 345 | switch (k) { 346 | case 'complete': 347 | case 'duration': 348 | case 'easing': 349 | case 'bezier': 350 | continue 351 | } 352 | var v = getStyle(els[i], k), unit 353 | , tmp = fun(options[k]) ? options[k](els[i]) : options[k] 354 | if (typeof tmp == 'string' && 355 | rgbOhex.test(tmp) && 356 | !rgbOhex.test(v)) { 357 | delete options[k]; // remove key :( 358 | continue; // cannot animate colors like 'orange' or 'transparent' 359 | // only #xxx, #xxxxxx, rgb(n,n,n) 360 | } 361 | 362 | begin[i][k] = k == 'transform' ? parseTransform(v) : 363 | typeof tmp == 'string' && rgbOhex.test(tmp) ? 364 | toHex(v).slice(1) : 365 | parseFloat(v) 366 | end[i][k] = k == 'transform' ? parseTransform(tmp, begin[i][k]) : 367 | typeof tmp == 'string' && tmp.charAt(0) == '#' ? 368 | toHex(tmp).slice(1) : 369 | by(tmp, parseFloat(v)); 370 | // record original unit 371 | (typeof tmp == 'string') && (unit = tmp.match(numUnit)) && (units[i][k] = unit[1]) 372 | } 373 | } 374 | // ONE TWEEN TO RULE THEM ALL 375 | return tween.apply(els, [duration, function (pos, v, xy) { 376 | // normally not a fan of optimizing for() loops, but we want something 377 | // fast for animating 378 | for (i = els.length; i--;) { 379 | if (points) { 380 | xy = bezier(bez[i], pos) 381 | els[i].style.left = xy[0] + 'px' 382 | els[i].style.top = xy[1] + 'px' 383 | } 384 | for (var k in options) { 385 | v = getTweenVal(pos, units, begin, end, k, i) 386 | k == 'transform' ? 387 | els[i].style[transform] = formatTransform(v) : 388 | k == 'opacity' && !opasity ? 389 | (els[i].style.filter = 'alpha(opacity=' + (v * 100) + ')') : 390 | (els[i].style[camelize(k)] = v) 391 | } 392 | } 393 | }, complete, ease]) 394 | } 395 | 396 | // expose useful methods 397 | morpheus.tween = tween 398 | morpheus.getStyle = getStyle 399 | morpheus.bezier = bezier 400 | morpheus.transform = transform 401 | morpheus.parseTransform = parseTransform 402 | morpheus.formatTransform = formatTransform 403 | morpheus.animationFrame = frame 404 | morpheus.easings = {} 405 | 406 | return morpheus 407 | 408 | }); 409 | -------------------------------------------------------------------------------- /morpheus.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Morpheus - A Brilliant Animator 3 | * https://github.com/ded/morpheus - (c) Dustin Diaz 2011 4 | * License MIT 5 | */ 6 | !function(e,t){typeof define=="function"?define(t):typeof module!="undefined"?module.exports=t():this[e]=t()}("morpheus",function(){function E(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t);for(n=0;n=0&&(t=w.slice(n+1),w.length=n,w=w.concat(t))}function N(e,t){var n={},r;if(r=e.match(c))n.rotate=B(r[1],t?t.rotate:null);if(r=e.match(h))n.scale=B(r[1],t?t.scale:null);if(r=e.match(p))n.skewx=B(r[1],t?t.skewx:null),n.skewy=B(r[3],t?t.skewy:null);if(r=e.match(d))n.translatex=B(r[1],t?t.translatex:null),n.translatey=B(r[3],t?t.translatey:null);return n}function C(e){var t="";return"rotate"in e&&(t+="rotate("+e.rotate+"deg) "),"scale"in e&&(t+="scale("+e.scale+") "),"translatex"in e&&(t+="translate("+e.translatex+"px,"+e.translatey+"px) "),"skewx"in e&&(t+="skew("+e.skewx+"deg,"+e.skewy+"deg)"),t}function k(e,t,n){return"#"+(1<<24|e<<16|t<<8|n).toString(16).slice(1)}function L(e){var t=e.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return(t?k(t[1],t[2],t[3]):e).replace(/#(\w)(\w)(\w)$/,"#$1$1$2$2$3$3")}function A(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}function O(e){return typeof e=="function"}function M(e){return Math.sin(e*Math.PI/2)}function _(e,t,n,r,s,o){function d(e){var i=e-c;if(i>a||h)return o=isFinite(o)?o:1,h?p&&t(o):t(o),T(d),n&&n.apply(f);isFinite(o)?t(l*r(i/a)+s):t(r(i/a))}r=O(r)?r:j.easings[r]||M;var a=e||u,f=this,l=o-s,c=i(),h=0,p=0;return x(d),{stop:function(e){h=1,p=e,e||(n=null)}}}function D(e,t){var n=e.length,r=[],i,s;for(i=0;i15?15:s<0?0:s,r[i]=s.toString(16);return"#"+r.join("")}function H(e,t,n,r,i,s,o){if(i=="transform"){o={};for(var a in n[s][i])o[a]=a in r[s][i]?Math.round(((r[s][i][a]-n[s][i][a])*e+n[s][i][a])*u)/u:n[s][i][a];return o}return typeof n[s][i]=="string"?P(e,n[s][i],r[s][i]):(o=Math.round(((r[s][i]-n[s][i])*e+n[s][i])*u)/u,i in v||(o+=t[s][i]||"px"),o)}function B(e,t,n,r,i){return(n=f.exec(e))?(i=parseFloat(n[2]))&&t+(n[1]=="+"?1:-1)*i:parseFloat(e)}function j(e,t){var n=e?n=isFinite(e.length)?e:[e]:[],r,i=t.complete,s=t.duration,o=t.easing,u=t.bezier,f=[],c=[],h=[],p=[],d,v;u&&(d=t.left,v=t.top,delete t.right,delete t.bottom,delete t.left,delete t.top);for(r=n.length;r--;){f[r]={},c[r]={},h[r]={};if(u){var b=y(n[r],"left"),w=y(n[r],"top"),E=[B(O(d)?d(n[r]):d||0,parseFloat(b)),B(O(v)?v(n[r]):v||0,parseFloat(w))];p[r]=O(u)?u(n[r],E):u,p[r].push(E),p[r].unshift([parseInt(b,10),parseInt(w,10)])}for(var S in t){switch(S){case"complete":case"duration":case"easing":case"bezier":continue}var x=y(n[r],S),T,k=O(t[S])?t[S](n[r]):t[S];if(typeof k=="string"&&a.test(k)&&!a.test(x)){delete t[S];continue}f[r][S]=S=="transform"?N(x):typeof k=="string"&&a.test(k)?L(x).slice(1):parseFloat(x),c[r][S]=S=="transform"?N(k,f[r][S]):typeof k=="string"&&k.charAt(0)=="#"?L(k).slice(1):B(k,parseFloat(x)),typeof k=="string"&&(T=k.match(l))&&(h[r][S]=T[1])}}return _.apply(n,[s,function(e,i,s){for(r=n.length;r--;){u&&(s=D(p[r],e),n[r].style.left=s[0]+"px",n[r].style.top=s[1]+"px");for(var o in t)i=H(e,h,f,c,o,r),o=="transform"?n[r].style[m]=C(i):o=="opacity"&&!g?n[r].style.filter="alpha(opacity="+i*100+")":n[r].style[A(o)]=i}},i,o])}var e=document,t=window,n=t.performance,r=n&&(n.now||n.webkitNow||n.msNow||n.mozNow),i=r?function(){return r.call(n)}:function(){return+(new Date)},s=!1,o=e.documentElement,u=1e3,a=/^rgb\(|#/,f=/^([+\-])=([\d\.]+)/,l=/^(?:[\+\-]=?)?\d+(?:\.\d+)?(%|in|cm|mm|em|ex|pt|pc|px)$/,c=/rotate\(((?:[+\-]=)?([\-\d\.]+))deg\)/,h=/scale\(((?:[+\-]=)?([\d\.]+))\)/,p=/skew\(((?:[+\-]=)?([\-\d\.]+))deg, ?((?:[+\-]=)?([\-\d\.]+))deg\)/,d=/translate\(((?:[+\-]=)?([\-\d\.]+))px, ?((?:[+\-]=)?([\-\d\.]+))px\)/,v={lineHeight:1,zoom:1,zIndex:1,opacity:1,transform:1},m=function(){var t=e.createElement("a").style,n=["webkitTransform","MozTransform","OTransform","msTransform","Transform"],r;for(r=0;r1e12!=i()>1e12});var w=[];return j.tween=_,j.getStyle=y,j.bezier=D,j.transform=m,j.parseTransform=N,j.formatTransform=C,j.animationFrame=b,j.easings={},j}) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "morpheus", 3 | "description": "A Brilliant Animator", 4 | "version": "0.7.2", 5 | "homepage": "https://github.com/ded/morpheus", 6 | "author": "Dustin Diaz (http://dustindiaz.com)", 7 | "keywords": [ 8 | "ender", 9 | "animation", 10 | "motion", 11 | "css", 12 | "colors", 13 | "morph", 14 | "tween", 15 | "curve", 16 | "bezier", 17 | "transform", 18 | "skew", 19 | "rotate" 20 | ], 21 | "main": "./morpheus.js", 22 | "ender": "./src/ender.js", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/ded/morpheus.git" 26 | }, 27 | "devDependencies": { 28 | "sink-test": "1.x.x", 29 | "smoosh": "0.4.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/copyright.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Morpheus - A Brilliant Animator 3 | * https://github.com/ded/morpheus - (c) Dustin Diaz 2011 4 | * License MIT 5 | */ -------------------------------------------------------------------------------- /src/easings.js: -------------------------------------------------------------------------------- 1 | /* The equations defined here are open source under BSD License. 2 | * http://www.robertpenner.com/easing_terms_of_use.html (c) 2003 Robert Penner 3 | * Adapted to single time-based by 4 | * Brian Crescimanno 5 | * Ken Snyder 6 | */ 7 | var easings = { 8 | easeOut: function (t) { 9 | return Math.sin(t * Math.PI / 2); 10 | } 11 | 12 | , easeOutStrong: function (t) { 13 | return (t == 1) ? 1 : 1 - Math.pow(2, -10 * t); 14 | } 15 | 16 | , easeIn: function (t) { 17 | return t * t; 18 | } 19 | 20 | , easeInStrong: function (t) { 21 | return (t == 0) ? 0 : Math.pow(2, 10 * (t - 1)); 22 | } 23 | 24 | , easeOutBounce: function(pos) { 25 | if ((pos) < (1/2.75)) { 26 | return (7.5625*pos*pos); 27 | } else if (pos < (2/2.75)) { 28 | return (7.5625*(pos-=(1.5/2.75))*pos + .75); 29 | } else if (pos < (2.5/2.75)) { 30 | return (7.5625*(pos-=(2.25/2.75))*pos + .9375); 31 | } else { 32 | return (7.5625*(pos-=(2.625/2.75))*pos + .984375); 33 | } 34 | } 35 | 36 | , easeInBack: function(pos){ 37 | var s = 1.70158; 38 | return (pos)*pos*((s+1)*pos - s); 39 | } 40 | 41 | , easeOutBack: function(pos){ 42 | var s = 1.70158; 43 | return (pos=pos-1)*pos*((s+1)*pos + s) + 1; 44 | } 45 | 46 | , bounce: function (t) { 47 | if (t < (1 / 2.75)) { 48 | return 7.5625 * t * t; 49 | } 50 | if (t < (2 / 2.75)) { 51 | return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; 52 | } 53 | if (t < (2.5 / 2.75)) { 54 | return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; 55 | } 56 | return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; 57 | } 58 | 59 | , bouncePast: function (pos) { 60 | if (pos < (1 / 2.75)) { 61 | return (7.5625 * pos * pos); 62 | } else if (pos < (2 / 2.75)) { 63 | return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + .75); 64 | } else if (pos < (2.5 / 2.75)) { 65 | return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + .9375); 66 | } else { 67 | return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + .984375); 68 | } 69 | } 70 | 71 | , swingTo: function(pos) { 72 | var s = 1.70158; 73 | return (pos -= 1) * pos * ((s + 1) * pos + s) + 1; 74 | } 75 | 76 | , swingFrom: function (pos) { 77 | var s = 1.70158; 78 | return pos * pos * ((s + 1) * pos - s); 79 | } 80 | 81 | , elastic: function(pos) { 82 | return -1 * Math.pow(4, -8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1; 83 | } 84 | 85 | , spring: function(pos) { 86 | return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 87 | } 88 | 89 | , blink: function(pos, blinks) { 90 | return Math.round(pos*(blinks||5)) % 2; 91 | } 92 | 93 | , pulse: function(pos, pulses) { 94 | return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5; 95 | } 96 | 97 | , wobble: function(pos) { 98 | return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; 99 | } 100 | 101 | , sinusoidal: function(pos) { 102 | return (-Math.cos(pos*Math.PI)/2) + 0.5; 103 | } 104 | 105 | , flicker: function(pos) { 106 | var pos = pos + (Math.random()-0.5)/5; 107 | return easings.sinusoidal(pos < 0 ? 0 : pos > 1 ? 1 : pos); 108 | } 109 | 110 | , mirror: function(pos) { 111 | if (pos < 0.5) 112 | return easings.sinusoidal(pos*2); 113 | else 114 | return easings.sinusoidal(1-(pos-0.5)*2); 115 | } 116 | 117 | }; -------------------------------------------------------------------------------- /src/ender.js: -------------------------------------------------------------------------------- 1 | var morpheus = require('morpheus') 2 | !function ($) { 3 | $.ender({ 4 | animate: function (options) { 5 | return morpheus(this, options) 6 | } 7 | , fadeIn: function (d, fn) { 8 | return morpheus(this, { 9 | duration: d 10 | , opacity: 1 11 | , complete: fn 12 | }) 13 | } 14 | , fadeOut: function (d, fn) { 15 | return morpheus(this, { 16 | duration: d 17 | , opacity: 0 18 | , complete: fn 19 | }) 20 | } 21 | }, true) 22 | $.ender({ 23 | tween: morpheus.tween 24 | }) 25 | }(ender) -------------------------------------------------------------------------------- /src/morpheus.js: -------------------------------------------------------------------------------- 1 | !function (name, definition) { 2 | if (typeof define == 'function') define(definition) 3 | else if (typeof module != 'undefined') module.exports = definition() 4 | else this[name] = definition() 5 | }('morpheus', function () { 6 | 7 | var doc = document 8 | , win = window 9 | , perf = win.performance 10 | , perfNow = perf && (perf.now || perf.webkitNow || perf.msNow || perf.mozNow) 11 | , now = perfNow ? function () { return perfNow.call(perf) } : function () { return +new Date() } 12 | , fixTs = false // feature detected below 13 | , html = doc.documentElement 14 | , thousand = 1000 15 | , rgbOhex = /^rgb\(|#/ 16 | , relVal = /^([+\-])=([\d\.]+)/ 17 | , numUnit = /^(?:[\+\-]=?)?\d+(?:\.\d+)?(%|in|cm|mm|em|ex|pt|pc|px)$/ 18 | , rotate = /rotate\(((?:[+\-]=)?([\-\d\.]+))deg\)/ 19 | , scale = /scale\(((?:[+\-]=)?([\d\.]+))\)/ 20 | , skew = /skew\(((?:[+\-]=)?([\-\d\.]+))deg, ?((?:[+\-]=)?([\-\d\.]+))deg\)/ 21 | , translate = /translate\(((?:[+\-]=)?([\-\d\.]+))px, ?((?:[+\-]=)?([\-\d\.]+))px\)/ 22 | // these elements do not require 'px' 23 | , unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, transform: 1} 24 | 25 | // which property name does this browser use for transform 26 | var transform = function () { 27 | var styles = doc.createElement('a').style 28 | , props = ['webkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'Transform'] 29 | , i 30 | for (i = 0; i < props.length; i++) { 31 | if (props[i] in styles) return props[i] 32 | } 33 | }() 34 | 35 | // does this browser support the opacity property? 36 | var opasity = function () { 37 | return typeof doc.createElement('a').style.opacity !== 'undefined' 38 | }() 39 | 40 | // initial style is determined by the elements themselves 41 | var getStyle = doc.defaultView && doc.defaultView.getComputedStyle ? 42 | function (el, property) { 43 | property = property == 'transform' ? transform : property 44 | property = camelize(property) 45 | var value = null 46 | , computed = doc.defaultView.getComputedStyle(el, '') 47 | computed && (value = computed[property]) 48 | return el.style[property] || value 49 | } : html.currentStyle ? 50 | 51 | function (el, property) { 52 | property = camelize(property) 53 | 54 | if (property == 'opacity') { 55 | var val = 100 56 | try { 57 | val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity 58 | } catch (e1) { 59 | try { 60 | val = el.filters('alpha').opacity 61 | } catch (e2) {} 62 | } 63 | return val / 100 64 | } 65 | var value = el.currentStyle ? el.currentStyle[property] : null 66 | return el.style[property] || value 67 | } : 68 | function (el, property) { 69 | return el.style[camelize(property)] 70 | } 71 | 72 | var frame = function () { 73 | // native animation frames 74 | // http://webstuff.nfshost.com/anim-timing/Overview.html 75 | // http://dev.chromium.org/developers/design-documents/requestanimationframe-implementation 76 | return win.requestAnimationFrame || 77 | win.webkitRequestAnimationFrame || 78 | win.mozRequestAnimationFrame || 79 | win.msRequestAnimationFrame || 80 | win.oRequestAnimationFrame || 81 | function (callback) { 82 | win.setTimeout(function () { 83 | callback(+new Date()) 84 | }, 17) // when I was 17.. 85 | } 86 | }() 87 | 88 | frame(function(timestamp) { 89 | // feature-detect if rAF and now() are of the same scale (epoch or high-res), 90 | // if not, we have to do a timestamp fix on each frame 91 | fixTs = timestamp > 1e12 != now() > 1e12 92 | }) 93 | 94 | var children = [] 95 | 96 | function has(array, elem, i) { 97 | if (Array.prototype.indexOf) return array.indexOf(elem) 98 | for (i = 0; i < array.length; ++i) { 99 | if (array[i] === elem) return i 100 | } 101 | } 102 | 103 | function render(timestamp) { 104 | var i, count = children.length 105 | if (fixTs) timestamp = now() 106 | for (i = count; i--;) { 107 | children[i](timestamp) 108 | } 109 | children.length && frame(render) 110 | } 111 | 112 | function live(f) { 113 | if (children.push(f) === 1) frame(render) 114 | } 115 | 116 | function die(f) { 117 | var rest, index = has(children, f) 118 | if (index >= 0) { 119 | rest = children.slice(index + 1) 120 | children.length = index 121 | children = children.concat(rest) 122 | } 123 | } 124 | 125 | function parseTransform(style, base) { 126 | var values = {}, m 127 | if (m = style.match(rotate)) values.rotate = by(m[1], base ? base.rotate : null) 128 | if (m = style.match(scale)) values.scale = by(m[1], base ? base.scale : null) 129 | if (m = style.match(skew)) {values.skewx = by(m[1], base ? base.skewx : null); values.skewy = by(m[3], base ? base.skewy : null)} 130 | if (m = style.match(translate)) {values.translatex = by(m[1], base ? base.translatex : null); values.translatey = by(m[3], base ? base.translatey : null)} 131 | return values 132 | } 133 | 134 | function formatTransform(v) { 135 | var s = '' 136 | if ('rotate' in v) s += 'rotate(' + v.rotate + 'deg) ' 137 | if ('scale' in v) s += 'scale(' + v.scale + ') ' 138 | if ('translatex' in v) s += 'translate(' + v.translatex + 'px,' + v.translatey + 'px) ' 139 | if ('skewx' in v) s += 'skew(' + v.skewx + 'deg,' + v.skewy + 'deg)' 140 | return s 141 | } 142 | 143 | function rgb(r, g, b) { 144 | return '#' + (1 << 24 | r << 16 | g << 8 | b).toString(16).slice(1) 145 | } 146 | 147 | // convert rgb and short hex to long hex 148 | function toHex(c) { 149 | var m = c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/) 150 | return (m ? rgb(m[1], m[2], m[3]) : c) 151 | .replace(/#(\w)(\w)(\w)$/, '#$1$1$2$2$3$3') // short skirt to long jacket 152 | } 153 | 154 | // change font-size => fontSize etc. 155 | function camelize(s) { 156 | return s.replace(/-(.)/g, function (m, m1) { 157 | return m1.toUpperCase() 158 | }) 159 | } 160 | 161 | // aren't we having it? 162 | function fun(f) { 163 | return typeof f == 'function' 164 | } 165 | 166 | function nativeTween(t) { 167 | // default to a pleasant-to-the-eye easeOut (like native animations) 168 | return Math.sin(t * Math.PI / 2) 169 | } 170 | 171 | /** 172 | * Core tween method that requests each frame 173 | * @param duration: time in milliseconds. defaults to 1000 174 | * @param fn: tween frame callback function receiving 'position' 175 | * @param done {optional}: complete callback function 176 | * @param ease {optional}: easing method. defaults to easeOut 177 | * @param from {optional}: integer to start from 178 | * @param to {optional}: integer to end at 179 | * @returns method to stop the animation 180 | */ 181 | function tween(duration, fn, done, ease, from, to) { 182 | ease = fun(ease) ? ease : morpheus.easings[ease] || nativeTween 183 | var time = duration || thousand 184 | , self = this 185 | , diff = to - from 186 | , start = now() 187 | , stop = 0 188 | , end = 0 189 | 190 | function run(t) { 191 | var delta = t - start 192 | if (delta > time || stop) { 193 | to = isFinite(to) ? to : 1 194 | stop ? end && fn(to) : fn(to) 195 | die(run) 196 | return done && done.apply(self) 197 | } 198 | // if you don't specify a 'to' you can use tween as a generic delta tweener 199 | // cool, eh? 200 | isFinite(to) ? 201 | fn((diff * ease(delta / time)) + from) : 202 | fn(ease(delta / time)) 203 | } 204 | 205 | live(run) 206 | 207 | return { 208 | stop: function (jump) { 209 | stop = 1 210 | end = jump // jump to end of animation? 211 | if (!jump) done = null // remove callback if not jumping to end 212 | } 213 | } 214 | } 215 | 216 | /** 217 | * generic bezier method for animating x|y coordinates 218 | * minimum of 2 points required (start and end). 219 | * first point start, last point end 220 | * additional control points are optional (but why else would you use this anyway ;) 221 | * @param points: array containing control points 222 | [[0, 0], [100, 200], [200, 100]] 223 | * @param pos: current be(tween) position represented as float 0 - 1 224 | * @return [x, y] 225 | */ 226 | function bezier(points, pos) { 227 | var n = points.length, r = [], i, j 228 | for (i = 0; i < n; ++i) { 229 | r[i] = [points[i][0], points[i][1]] 230 | } 231 | for (j = 1; j < n; ++j) { 232 | for (i = 0; i < n - j; ++i) { 233 | r[i][0] = (1 - pos) * r[i][0] + pos * r[parseInt(i + 1, 10)][0] 234 | r[i][1] = (1 - pos) * r[i][1] + pos * r[parseInt(i + 1, 10)][1] 235 | } 236 | } 237 | return [r[0][0], r[0][1]] 238 | } 239 | 240 | // this gets you the next hex in line according to a 'position' 241 | function nextColor(pos, start, finish) { 242 | var r = [], i, e, from, to 243 | for (i = 0; i < 6; i++) { 244 | from = Math.min(15, parseInt(start.charAt(i), 16)) 245 | to = Math.min(15, parseInt(finish.charAt(i), 16)) 246 | e = Math.floor((to - from) * pos + from) 247 | e = e > 15 ? 15 : e < 0 ? 0 : e 248 | r[i] = e.toString(16) 249 | } 250 | return '#' + r.join('') 251 | } 252 | 253 | // this retreives the frame value within a sequence 254 | function getTweenVal(pos, units, begin, end, k, i, v) { 255 | if (k == 'transform') { 256 | v = {} 257 | for (var t in begin[i][k]) { 258 | v[t] = (t in end[i][k]) ? Math.round(((end[i][k][t] - begin[i][k][t]) * pos + begin[i][k][t]) * thousand) / thousand : begin[i][k][t] 259 | } 260 | return v 261 | } else if (typeof begin[i][k] == 'string') { 262 | return nextColor(pos, begin[i][k], end[i][k]) 263 | } else { 264 | // round so we don't get crazy long floats 265 | v = Math.round(((end[i][k] - begin[i][k]) * pos + begin[i][k]) * thousand) / thousand 266 | // some css properties don't require a unit (like zIndex, lineHeight, opacity) 267 | if (!(k in unitless)) v += units[i][k] || 'px' 268 | return v 269 | } 270 | } 271 | 272 | // support for relative movement via '+=n' or '-=n' 273 | function by(val, start, m, r, i) { 274 | return (m = relVal.exec(val)) ? 275 | (i = parseFloat(m[2])) && (start + (m[1] == '+' ? 1 : -1) * i) : 276 | parseFloat(val) 277 | } 278 | 279 | /** 280 | * morpheus: 281 | * @param element(s): HTMLElement(s) 282 | * @param options: mixed bag between CSS Style properties & animation options 283 | * - {n} CSS properties|values 284 | * - value can be strings, integers, 285 | * - or callback function that receives element to be animated. method must return value to be tweened 286 | * - relative animations start with += or -= followed by integer 287 | * - duration: time in ms - defaults to 1000(ms) 288 | * - easing: a transition method - defaults to an 'easeOut' algorithm 289 | * - complete: a callback method for when all elements have finished 290 | * - bezier: array of arrays containing x|y coordinates that define the bezier points. defaults to none 291 | * - this may also be a function that receives element to be animated. it must return a value 292 | */ 293 | function morpheus(elements, options) { 294 | var els = elements ? (els = isFinite(elements.length) ? elements : [elements]) : [], i 295 | , complete = options.complete 296 | , duration = options.duration 297 | , ease = options.easing 298 | , points = options.bezier 299 | , begin = [] 300 | , end = [] 301 | , units = [] 302 | , bez = [] 303 | , originalLeft 304 | , originalTop 305 | 306 | if (points) { 307 | // remember the original values for top|left 308 | originalLeft = options.left; 309 | originalTop = options.top; 310 | delete options.right; 311 | delete options.bottom; 312 | delete options.left; 313 | delete options.top; 314 | } 315 | 316 | for (i = els.length; i--;) { 317 | 318 | // record beginning and end states to calculate positions 319 | begin[i] = {} 320 | end[i] = {} 321 | units[i] = {} 322 | 323 | // are we 'moving'? 324 | if (points) { 325 | 326 | var left = getStyle(els[i], 'left') 327 | , top = getStyle(els[i], 'top') 328 | , xy = [by(fun(originalLeft) ? originalLeft(els[i]) : originalLeft || 0, parseFloat(left)), 329 | by(fun(originalTop) ? originalTop(els[i]) : originalTop || 0, parseFloat(top))] 330 | 331 | bez[i] = fun(points) ? points(els[i], xy) : points 332 | bez[i].push(xy) 333 | bez[i].unshift([ 334 | parseInt(left, 10), 335 | parseInt(top, 10) 336 | ]) 337 | } 338 | 339 | for (var k in options) { 340 | switch (k) { 341 | case 'complete': 342 | case 'duration': 343 | case 'easing': 344 | case 'bezier': 345 | continue 346 | } 347 | var v = getStyle(els[i], k), unit 348 | , tmp = fun(options[k]) ? options[k](els[i]) : options[k] 349 | if (typeof tmp == 'string' && 350 | rgbOhex.test(tmp) && 351 | !rgbOhex.test(v)) { 352 | delete options[k]; // remove key :( 353 | continue; // cannot animate colors like 'orange' or 'transparent' 354 | // only #xxx, #xxxxxx, rgb(n,n,n) 355 | } 356 | 357 | begin[i][k] = k == 'transform' ? parseTransform(v) : 358 | typeof tmp == 'string' && rgbOhex.test(tmp) ? 359 | toHex(v).slice(1) : 360 | parseFloat(v) 361 | end[i][k] = k == 'transform' ? parseTransform(tmp, begin[i][k]) : 362 | typeof tmp == 'string' && tmp.charAt(0) == '#' ? 363 | toHex(tmp).slice(1) : 364 | by(tmp, parseFloat(v)); 365 | // record original unit 366 | (typeof tmp == 'string') && (unit = tmp.match(numUnit)) && (units[i][k] = unit[1]) 367 | } 368 | } 369 | // ONE TWEEN TO RULE THEM ALL 370 | return tween.apply(els, [duration, function (pos, v, xy) { 371 | // normally not a fan of optimizing for() loops, but we want something 372 | // fast for animating 373 | for (i = els.length; i--;) { 374 | if (points) { 375 | xy = bezier(bez[i], pos) 376 | els[i].style.left = xy[0] + 'px' 377 | els[i].style.top = xy[1] + 'px' 378 | } 379 | for (var k in options) { 380 | v = getTweenVal(pos, units, begin, end, k, i) 381 | k == 'transform' ? 382 | els[i].style[transform] = formatTransform(v) : 383 | k == 'opacity' && !opasity ? 384 | (els[i].style.filter = 'alpha(opacity=' + (v * 100) + ')') : 385 | (els[i].style[camelize(k)] = v) 386 | } 387 | } 388 | }, complete, ease]) 389 | } 390 | 391 | // expose useful methods 392 | morpheus.tween = tween 393 | morpheus.getStyle = getStyle 394 | morpheus.bezier = bezier 395 | morpheus.transform = transform 396 | morpheus.parseTransform = parseTransform 397 | morpheus.formatTransform = formatTransform 398 | morpheus.animationFrame = frame 399 | morpheus.easings = {} 400 | 401 | return morpheus 402 | 403 | }); 404 | -------------------------------------------------------------------------------- /src/rtltr.js: -------------------------------------------------------------------------------- 1 | !function (context) { 2 | var m = context.morpheus, 3 | map = { 4 | 'padding-left': 1 5 | , 'paddingLeft': 1 6 | , 'padding-right': 1 7 | , 'paddingRight': 1 8 | , 'margin-left': 1 9 | , 'marginLeft': 1 10 | , 'margin-right': 1 11 | , 'marginRight': 1 12 | , 'border-left-width': 1 13 | , 'borderLeftWidth': 1 14 | , 'border-right-width': 1 15 | , 'borderRightWidth': 1 16 | , 'left': 1 17 | , 'right': 1 18 | } 19 | context.morpheus = function (elements, options, k, i, v) { 20 | if (context.morpheus.normal) { 21 | return m(elements, options) 22 | } 23 | for (k in options) { 24 | if (options.hasOwnProperty(k) && map[k]) { 25 | v = options[k] 26 | delete options[k] 27 | options[k.replace(/left|right/ig, function (m) { 28 | return /left/i.exec(m) ? 'Right' : 'Left' 29 | })] = v 30 | } 31 | } 32 | return m(elements, options) 33 | } 34 | }(this); -------------------------------------------------------------------------------- /tests/tests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Morpheus Tests 5 | 6 | 17 | 18 | 19 | 20 | 21 | 22 |

Morpheus Tests

23 |
24 |
25 |
26 |
    27 | 28 | 260 | 261 | 262 | 263 | --------------------------------------------------------------------------------