transition-native.js revision bf3c648d6ab588c8b04fb824a7281898c469bd67
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinYUI.add('transition-native', function(Y) {
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews
75c0816e8295e180f4bc7f10db3d0d880383bc1cMark Andrews/**
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein* The Native Transition Utility provides an API wrapper for CSS transitions.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein* It is also the base module for the timer-based transition module.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein* @module transition
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein*/
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein/**
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein* Provides the base Transition class. The "transition" method is added to Node,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein* and is how Transition should be used.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein*
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein* @module transition
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein* @submodule transition-native
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein*/
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews/**
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * A class for constructing transition instances.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * Adds the "transition" method to Node.
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * @class Transition
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein * @constructor
e21a2904f02a03fa06b6db04d348f65fe9c67b2bMark Andrews * @see Node
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein */
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austeinvar TRANSITION = '-webkit-transition',
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein TRANSITION_CAMEL = 'WebkitTransition',
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein TRANSITION_PROPERTY = '-webkit-transition-property',
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein TRANSITION_DURATION = '-webkit-transition-duration',
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein TRANSITION_TIMING_FUNCTION = '-webkit-transition-timing-function',
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein TRANSITION_DELAY = '-webkit-transition-delay',
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews TRANSITION_END = 'webkitTransitionEnd',
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein TRANSFORM_CAMEL = 'WebkitTransform',
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition = function() {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein this.init.apply(this, arguments);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein};
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition._toCamel = function(property) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein property = property.replace(/-([a-z])/gi, function(m0, m1) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return m1.toUpperCase();
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein });
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return property;
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews};
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition._toHyphen = function(property) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) {
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews var str = '';
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews if (m1) {
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews str += '-' + m1.toLowerCase();
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews }
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews str += m2;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (m3) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein str += '-' + m3.toLowerCase();
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein }
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return str;
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews });
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return property;
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews};
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
71c66a876ecca77923638d3f94cc0783152b2f03Mark AndrewsTransition._reKeywords = /^(?:node|duration|iterations|easing|delay)$/;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition.useNative = false;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrewsif (TRANSITION in Y.config.doc.documentElement.style) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Transition.useNative = true;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Transition.supported = true; // TODO: remove
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein}
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinY.Node.DOM_EVENTS[TRANSITION_END] = 1;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
b0e8629055a766d4555a005a283c2889a5974945Mark AndrewsTransition.NAME = 'transition';
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
71c66a876ecca77923638d3f94cc0783152b2f03Mark AndrewsTransition.DEFAULT_EASING = 'ease-in-out';
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition.DEFAULT_DURATION = 0.5;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition.DEFAULT_DELAY = 0;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition._nodeAttrs = {};
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition._count = 0;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob AusteinTransition.prototype = {
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews constructor: Transition,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein init: function(node, config) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein var anim = this;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (!anim._running) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._node = node;
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews anim._config = config;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein node._transition = anim; // cache for reuse
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._duration = ('duration' in config) ?
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein config.duration: anim.constructor.DEFAULT_DURATION;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._delay = ('delay' in config) ?
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein config.delay: anim.constructor.DEFAULT_DELAY;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._easing = config.easing || anim.constructor.DEFAULT_EASING;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._count = 0; // track number of animated properties
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._running = false;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim.initAttrs(config);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein }
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein return anim;
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews },
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein initAttrs: function(config) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein var anim = this,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein node = anim._node,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein uid = Y.stamp(node),
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein attrs = Transition._nodeAttrs[uid],
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein duration,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein delay,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein easing,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein val,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein transition,
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein attr;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (!attrs) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein attrs = Transition._nodeAttrs[uid] = {};
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein }
b0e8629055a766d4555a005a283c2889a5974945Mark Andrews
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (config.transform && !config[TRANSFORM_CAMEL]) {
71c66a876ecca77923638d3f94cc0783152b2f03Mark Andrews config[TRANSFORM_CAMEL] = config.transform;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein delete config.transform; // TODO: copy
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrews }
5a4557e8de2951a2796676b5ec4b6a90caa5be14Mark Andrews
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein for (attr in config) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (config.hasOwnProperty(attr) && !Transition._reKeywords.test(attr)) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein val = transition = config[attr];
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (attrs[attr] && attrs[attr].transition) {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein attrs[attr].transition._count--; // remapping attr to this transition
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein } else {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein Transition._count += 1;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein }
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (typeof transition.value !== 'undefined') {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein val = transition.value;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein }
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (typeof val === 'function') {
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein val = val.call(node, node);
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein }
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein duration = (typeof transition.duration !== 'undefined') ? transition.duration :
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._duration;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein delay = (typeof transition.delay !== 'undefined') ? transition.delay :
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein anim._delay;
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein if (!duration) { // make async and fire events
60e5e10f8d2e2b0c41e8abad38cacd867caa6ab2Rob Austein duration = 0.00001;
}
easing = transition.easing || anim._easing;
anim._count++; // track number of bound properties
attrs[attr] = {
value: val,
duration: duration,
delay: delay,
easing: easing,
transition: anim
};
}
}
},
/**
* Starts or an animation.
* @method run
* @chainable
*/
run: function(callback) {
var anim = this;
if (!anim._running) {
anim._running = true;
anim._node.fire('transition:start', {
type: 'transition:start',
config: anim._config
});
anim._start();
anim._callback = callback;
}
return anim;
},
_start: function() {
this._runNative();
},
_prepDur: function(dur) {
dur = parseFloat(dur);
return dur + 's';
},
_runNative: function(time) {
var anim = this,
node = anim._node,
uid = Y.stamp(node),
domNode = node._node,
style = domNode.style,
computed = getComputedStyle(domNode),
attrs = Transition._nodeAttrs[uid],
cssText = '',
cssTransition = computed[TRANSITION_PROPERTY],
transitionText = TRANSITION_PROPERTY + ': ',
duration = TRANSITION_DURATION + ': ',
easing = TRANSITION_TIMING_FUNCTION + ': ',
delay = TRANSITION_DELAY + ': ',
hyphy,
attr,
name;
// preserve existing transitions
if (cssTransition !== 'all') {
transitionText += cssTransition + ',';
duration += computed[TRANSITION_DURATION] + ',';
easing += computed[TRANSITION_TIMING_FUNCTION] + ',';
delay += computed[TRANSITION_DELAY] + ',';
}
// run transitions mapped to this instance
for (name in attrs) {
hyphy = Transition._toHyphen(name);
attr = attrs[name];
if (attrs.hasOwnProperty(name) && attr.transition === anim) {
if (name in domNode.style) { // only native styles allowed
duration += anim._prepDur(attr.duration) + ',';
delay += anim._prepDur(attr.delay) + ',';
easing += (attr.easing) + ',';
transitionText += hyphy + ',';
cssText += hyphy + ': ' + attr.value + '; ';
} else {
delete attrs[name];
anim._count--;
Transition._count--;
}
}
}
transitionText = transitionText.replace(/,$/, ';');
duration = duration.replace(/,$/, ';');
easing = easing.replace(/,$/, ';');
delay = delay.replace(/,$/, ';');
// only one native end event per node
if (!node._hasTransitionEnd) {
node.on(TRANSITION_END, anim._onNativeEnd);
node._hasTransitionEnd = true;
}
style.cssText += transitionText + duration + easing + delay + cssText;
},
_end: function(elapsed) {
var anim = this,
node = anim._node,
callback = anim._callback,
data = {
type: 'transition:end',
config: anim._config,
elapsedTime: elapsed
};
anim._running = false;
if (callback) {
anim._callback = null;
setTimeout(function() { // IE: allow previous update to finish
callback.call(node, data);
}, 1);
}
node.fire('transition:end', data);
},
_endNative: function() {
var node = this._node;
if (Transition._count <= 0) {
node._node.style[TRANSITION_CAMEL] = '';
}
},
_onNativeEnd: function(e) {
var node = this,
uid = Y.stamp(node),
event = e._event,
name = Transition._toCamel(event.propertyName),
elapsed = event.elapsedTime,
attrs = Transition._nodeAttrs[uid],
attr = attrs[name],
anim = (attr) ? attr.transition :null,
callback;
if (anim) {
callback = anim._callback;
anim._count--;
delete attrs[name];
Transition._count--;
node.fire('transition:propertyEnd', {
type: 'propertyEnd',
propertyName: name,
elapsedTime: elapsed
});
if (anim._count <= 0) {
anim._endNative();
anim._end(elapsed);
}
}
},
destroy: function() {
this.detachAll();
this._node = null;
}
};
Y.Transition = Transition;
Y.TransitionNative = Transition; // TODO: remove
/**
Animate one or more css properties to a given value. Requires the "transition" module.
<pre>example usage:
Y.one('#demo').transition({
duration: 1, // seconds
easing: 'ease-out',
height: '10px',
width: '10px',
opacity: { // per property duration and/or easing
value: 0,
duration: 2,
easing: 'ease-in'
}
});
</pre>
@for Node
@method transition
@param {Object} An object containing one or more style properties, a duration and an easing.
@chainable
*/
Y.Node.prototype.transition = function(config, callback) {
var anim = this._transition;
if (anim && !anim._running) {
anim.init(this, config);
} else {
anim = new Transition(this, config);
}
anim.run(callback);
return this;
};
}, '@VERSION@' ,{requires:['node-base']});