emile
Version:
no-thrills stand-alone CSS animation JavaScript framework
220 lines (200 loc) • 6.13 kB
JavaScript
/*!
* emile.js (c) 2009 - 2011 Thomas Fuchs
* Licensed under the terms of the MIT license.
*/
!function (context) {
var parseEl = document.createElement('div'),
prefixes = ["webkit", "Moz", "O"],
j = 3,
prefix,
_prefix,
d = /\d+$/,
animationProperties = {},
baseProps = 'backgroundColor borderBottomColor borderLeftColor ' +
'borderRightColor borderTopColor color fontWeight lineHeight ' +
'opacity outlineColor zIndex',
pixelProps = 'top bottom left right ' +
'borderWidth borderBottomWidth borderLeftWidth borderRightWidth borderTopWidth ' +
'borderSpacing borderRadius ' +
'marginBottom marginLeft marginRight marginTop ' +
'width height ' +
'maxHeight maxWidth minHeight minWidth ' +
'paddingBottom paddingLeft paddingRight paddingTop ' +
'fontSize wordSpacing textIndent letterSpacing ' +
'outlineWidth outlineOffset',
props = (baseProps + ' ' + pixelProps).split(' ');
while (j--) {
_prefix = prefixes[j];
parseEl.style.cssText = "-" + _prefix.toLowerCase() + "-transition-property:opacity;";
if (typeof parseEl.style[_prefix + "TransitionProperty"] != "undefined") {
prefix = _prefix;
}
}
var transitionEnd = /^w/.test(prefix) ? 'webkitTransitionEnd' : 'transitionend';
for (var p = pixelProps.split(' '), i = p.length; i--;) {
animationProperties[p[i]] = 1;
}
function map(o, fn, scope) {
var a = [], i;
for (i in o) {
a.push(fn.call(scope, o[i], i, o));
}
return a;
}
function camelize(s) {
return s.replace(/-(.)/g, function (m, m1) {
return m1.toUpperCase();
});
}
function serialize(o, modify) {
return map(o, function (v, k) {
var kv = modify ? modify(k, v) : [k, v];
return kv[0] + ':' + kv[1] + ';';
}).join('');
}
function camelToDash(s) {
if (s.toUpperCase() === s) {
return s;
}
return s.replace(/([a-zA-Z0-9])([A-Z])/g, function (m, m1, m2) {
return (m1 + "-" + m2);
}).toLowerCase();
}
function interpolate(source, target, pos) {
return (source + (target - source) * pos).toFixed(3);
}
function s(str, p, c) {
return str.substr(p, c || 1);
}
function color(source, target, pos) {
var i = 2, j, c, tmp, v = [], r = [];
while ((j = 3) && (c = arguments[i - 1]) && i--) {
if (s(c, 0) == 'r') {
c = c.match(/\d+/g);
while (j--) {
v.push(~~c[j]);
}
} else {
if (c.length == 4) {
c = '#' + s(c, 1) + s(c, 1) + s(c, 2) + s(c, 2) + s(c, 3) + s(c, 3);
}
while (j--) {
v.push(parseInt(s(c, 1 + j * 2, 2), 16));
}
}
}
while (j--) {
tmp = ~~(v[j + 3] + (v[j] - v[j + 3]) * pos);
r.push(tmp < 0 ? 0 : tmp > 255 ? 255 : tmp);
}
return 'rgb(' + r.join(',') + ')';
}
function parse(prop) {
var p = parseFloat(prop), q = prop ? prop.replace(/^[\-\d\.]+/, '') : prop;
return isNaN(p) ?
{ v: q,
f: color,
u: ''
} :
{
v: p,
f: interpolate,
u: q
};
}
function normalize(style) {
var css, rules = {}, i = props.length, v;
parseEl.innerHTML = '<div style="' + style + '"></div>';
css = parseEl.childNodes[0].style;
while (i--) {
(v = css[props[i]]) && (rules[props[i]] = parse(v));
}
return rules;
}
function _emile(el, style, opts, after) {
opts = opts || {};
var target = normalize(style),
comp = el.currentStyle ? el.currentStyle : getComputedStyle(el, null),
current = {}, start = +new Date(), prop,
dur = opts.duration || 200, finish = start + dur, interval,
easing = opts.easing || function (pos) {
return (-Math.cos(pos * Math.PI) / 2) + 0.5;
};
for (prop in target) {
current[prop] = parse(comp[prop]);
}
interval = setInterval(function () {
var time = +new Date(), p, pos = time > finish ? 1 : (time - start) / dur;
for (p in target) {
el.style[p] = target[p].f(current[p].v, target[p].v, easing(pos)) + target[p].u;
}
if (time > finish) {
clearInterval(interval);
opts.after && opts.after();
after && setTimeout(after, 1);
}
}, 10);
}
function nativeAnim(el, o, opts) {
var props = [],
styles = [],
duration = opts.duration || 1000,
easing = opts.easing || 'ease-out',
newStyle = '';
duration = duration + 'ms';
el.addEventListener(transitionEnd, function f() {
el.setAttribute('style', newStyle);
if(opts.after) opts.after();
el.removeEventListener(transitionEnd, f, true);
}, true);
setTimeout(function () {
var k;
for (k in o) {
o.hasOwnProperty(k) && props.push(camelToDash(k) + ' ' + duration + ' ' + easing);
}
for (k in o) {
var v = (camelize(k) in animationProperties) && d.test(o[k]) ? o[k] + 'px' : o[k];
o.hasOwnProperty(k) && (el.style[camelize(k)] = v);
}
newStyle = el.getAttribute('style');
props = props.join(',');
el.style[prefix + 'Transition'] = props;
}, 10);
}
function clone(o) {
var r = {};
for (var k in o) {
r[k] = o[k];
(k == 'after') && delete o[k];
}
return r;
}
function emile(el, o) {
el = typeof el == 'string' ? document.getElementById(el) : el;
o = clone(o);
var opts = {
duration: o.duration,
easing: o.easing,
after: o.after
};
delete o.duration;
delete o.easing;
delete o.after;
if (prefix && (typeof opts.easing !== 'function')) {
return nativeAnim(el, o, opts);
}
var serial = serialize(o, function (k, v) {
k = camelToDash(k);
return (camelize(k) in animationProperties) && d.test(v) ?
[k, v + 'px'] :
[k, v];
});
_emile(el, serial, opts);
}
var old = context.emile;
emile.noConflict = function () {
context.emile = old;
return this;
};
context.emile = emile;
}(this);