AutorÃa | Ultima modificación | Ver Log |
YUI.add('transition', function (Y, NAME) {/*** Provides the transition method for Node.* Transition has no API of its own, but adds the transition method to Node.** @module transition* @requires node-style*/var CAMEL_VENDOR_PREFIX = '',VENDOR_PREFIX = '',DOCUMENT = Y.config.doc,DOCUMENT_ELEMENT = 'documentElement',DOCUMENT_STYLE = DOCUMENT[DOCUMENT_ELEMENT].style,TRANSITION_CAMEL = 'transition',TRANSITION_PROPERTY_CAMEL = 'transitionProperty',TRANSITION_PROPERTY,TRANSITION_DURATION,TRANSITION_TIMING_FUNCTION,TRANSITION_DELAY,TRANSITION_END,ON_TRANSITION_END,EMPTY_OBJ = {},VENDORS = ['Webkit','Moz'],VENDOR_TRANSITION_END = {Webkit: 'webkitTransitionEnd'},/*** A class for constructing transition instances.* Adds the "transition" method to Node.* @class Transition* @constructor*/Transition = function() {this.init.apply(this, arguments);};// One off handling of transform-prefixing.Transition._TRANSFORM = 'transform';Transition._toCamel = function(property) {property = property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase();});return property;};Transition._toHyphen = function(property) {property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) {var str = ((m1) ? '-' + m1.toLowerCase() : '') + m2;if (m3) {str += '-' + m3.toLowerCase();}return str;});return property;};Transition.SHOW_TRANSITION = 'fadeIn';Transition.HIDE_TRANSITION = 'fadeOut';Transition.useNative = false;// Map transition properties to vendor-specific versions.if ('transition' in DOCUMENT_STYLE&& 'transitionProperty' in DOCUMENT_STYLE&& 'transitionDuration' in DOCUMENT_STYLE&& 'transitionTimingFunction' in DOCUMENT_STYLE&& 'transitionDelay' in DOCUMENT_STYLE) {Transition.useNative = true;Transition.supported = true; // TODO: remove} else {Y.Array.each(VENDORS, function(val) { // then vendor specificvar property = val + 'Transition';if (property in DOCUMENT[DOCUMENT_ELEMENT].style) {CAMEL_VENDOR_PREFIX = val;VENDOR_PREFIX = Transition._toHyphen(val) + '-';Transition.useNative = true;Transition.supported = true; // TODO: removeTransition._VENDOR_PREFIX = val;}});}// Map transform property to vendor-specific versions.// One-off required for cssText injection.if (typeof DOCUMENT_STYLE.transform === 'undefined') {Y.Array.each(VENDORS, function(val) { // then vendor specificvar property = val + 'Transform';if (typeof DOCUMENT_STYLE[property] !== 'undefined') {Transition._TRANSFORM = property;}});}if (CAMEL_VENDOR_PREFIX) {TRANSITION_CAMEL = CAMEL_VENDOR_PREFIX + 'Transition';TRANSITION_PROPERTY_CAMEL = CAMEL_VENDOR_PREFIX + 'TransitionProperty';}TRANSITION_PROPERTY = VENDOR_PREFIX + 'transition-property';TRANSITION_DURATION = VENDOR_PREFIX + 'transition-duration';TRANSITION_TIMING_FUNCTION = VENDOR_PREFIX + 'transition-timing-function';TRANSITION_DELAY = VENDOR_PREFIX + 'transition-delay';TRANSITION_END = 'transitionend';ON_TRANSITION_END = 'on' + CAMEL_VENDOR_PREFIX.toLowerCase() + 'transitionend';TRANSITION_END = VENDOR_TRANSITION_END[CAMEL_VENDOR_PREFIX] || TRANSITION_END;Transition.fx = {};Transition.toggles = {};Transition._hasEnd = {};Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i;Y.Node.DOM_EVENTS[TRANSITION_END] = 1;Transition.NAME = 'transition';Transition.DEFAULT_EASING = 'ease';Transition.DEFAULT_DURATION = 0.5;Transition.DEFAULT_DELAY = 0;Transition._nodeAttrs = {};Transition.prototype = {constructor: Transition,init: function(node, config) {var anim = this;anim._node = node;if (!anim._running && config) {anim._config = config;node._transition = anim; // cache for reuseanim._duration = ('duration' in config) ?config.duration: anim.constructor.DEFAULT_DURATION;anim._delay = ('delay' in config) ?config.delay: anim.constructor.DEFAULT_DELAY;anim._easing = config.easing || anim.constructor.DEFAULT_EASING;anim._count = 0; // track number of animated propertiesanim._running = false;}return anim;},addProperty: function(prop, config) {var anim = this,node = this._node,uid = Y.stamp(node),nodeInstance = Y.one(node),attrs = Transition._nodeAttrs[uid],computed,compareVal,dur,attr,val;if (!attrs) {attrs = Transition._nodeAttrs[uid] = {};}attr = attrs[prop];// might just be a valueif (config && config.value !== undefined) {val = config.value;} else if (config !== undefined) {val = config;config = EMPTY_OBJ;}if (typeof val === 'function') {val = val.call(nodeInstance, nodeInstance);}if (attr && attr.transition) {// take control if another transition owns this propertyif (attr.transition !== anim) {attr.transition._count--; // remapping attr to this transition}}anim._count++; // properties per transition// make 0 async and fire eventsdur = ((typeof config.duration !== 'undefined') ? config.duration :anim._duration) || 0.0001;attrs[prop] = {value: val,duration: dur,delay: (typeof config.delay !== 'undefined') ? config.delay :anim._delay,easing: config.easing || anim._easing,transition: anim};// native end event doesnt fire when setting to same value// supplementing with timer// val may be a string or number (height: 0, etc), but computedStyle is always stringcomputed = Y.DOM.getComputedStyle(node, prop);compareVal = (typeof val === 'string') ? computed : parseFloat(computed);if (Transition.useNative && compareVal === val) {setTimeout(function() {anim._onNativeEnd.call(node, {propertyName: prop,elapsedTime: dur});}, dur * 1000);}},removeProperty: function(prop) {var anim = this,attrs = Transition._nodeAttrs[Y.stamp(anim._node)];if (attrs && attrs[prop]) {delete attrs[prop];anim._count--;}},initAttrs: function(config) {var attr,node = this._node;if (config.transform && !config[Transition._TRANSFORM]) {config[Transition._TRANSFORM] = config.transform;delete config.transform; // TODO: copy}for (attr in config) {if (config.hasOwnProperty(attr) && !Transition._reKeywords.test(attr)) {this.addProperty(attr, config[attr]);// when size is auto or % webkit starts from zero instead of computed// (https://bugs.webkit.org/show_bug.cgi?id=16020)// TODO: selective setif (node.style[attr] === '') {Y.DOM.setStyle(node, attr, Y.DOM.getComputedStyle(node, attr));}}}},/*** Starts or an animation.* @method run* @chainable* @private*/run: function(callback) {var anim = this,node = anim._node,config = anim._config,data = {type: 'transition:start',config: config};if (!anim._running) {anim._running = true;if (config.on && config.on.start) {config.on.start.call(Y.one(node), data);}anim.initAttrs(anim._config);anim._callback = callback;anim._start();}return anim;},_start: function() {this._runNative();},_prepDur: function(dur) {dur = parseFloat(dur) * 1000;return dur + 'ms';},_runNative: function() {var anim = this,node = anim._node,uid = Y.stamp(node),style = node.style,computed = node.ownerDocument.defaultView.getComputedStyle(node),attrs = Transition._nodeAttrs[uid],cssText = '',cssTransition = computed[Transition._toCamel(TRANSITION_PROPERTY)],transitionText = TRANSITION_PROPERTY + ': ',duration = TRANSITION_DURATION + ': ',easing = TRANSITION_TIMING_FUNCTION + ': ',delay = TRANSITION_DELAY + ': ',hyphy,attr,name;// preserve existing transitionsif (cssTransition !== 'all') {transitionText += cssTransition + ',';duration += computed[Transition._toCamel(TRANSITION_DURATION)] + ',';easing += computed[Transition._toCamel(TRANSITION_TIMING_FUNCTION)] + ',';delay += computed[Transition._toCamel(TRANSITION_DELAY)] + ',';}// run transitions mapped to this instancefor (name in attrs) {hyphy = Transition._toHyphen(name);attr = attrs[name];if ((attr = attrs[name]) && attr.transition === anim) {if (name in node.style) { // only native styles allowedduration += anim._prepDur(attr.duration) + ',';delay += anim._prepDur(attr.delay) + ',';easing += (attr.easing) + ',';transitionText += hyphy + ',';cssText += hyphy + ': ' + attr.value + '; ';} else {this.removeProperty(name);}}}transitionText = transitionText.replace(/,$/, ';');duration = duration.replace(/,$/, ';');easing = easing.replace(/,$/, ';');delay = delay.replace(/,$/, ';');// only one native end event per nodeif (!Transition._hasEnd[uid]) {node.addEventListener(TRANSITION_END, anim._onNativeEnd, '');Transition._hasEnd[uid] = true;}style.cssText += transitionText + duration + easing + delay + cssText;},_end: function(elapsed) {var anim = this,node = anim._node,callback = anim._callback,config = anim._config,data = {type: 'transition:end',config: config,elapsedTime: elapsed},nodeInstance = Y.one(node);anim._running = false;anim._callback = null;if (node) {if (config.on && config.on.end) {setTimeout(function() { // IE: allow previous update to finishconfig.on.end.call(nodeInstance, data);// nested to ensure proper fire orderif (callback) {callback.call(nodeInstance, data);}}, 1);} else if (callback) {setTimeout(function() { // IE: allow previous update to finishcallback.call(nodeInstance, data);}, 1);}}},_endNative: function(name) {var node = this._node,value = node.ownerDocument.defaultView.getComputedStyle(node, '')[Transition._toCamel(TRANSITION_PROPERTY)];name = Transition._toHyphen(name);if (typeof value === 'string') {value = value.replace(new RegExp('(?:^|,\\s)' + name + ',?'), ',');value = value.replace(/^,|,$/, '');node.style[TRANSITION_CAMEL] = value;}},_onNativeEnd: function(e) {var node = this,uid = Y.stamp(node),event = e,//e._event,name = Transition._toCamel(event.propertyName),elapsed = event.elapsedTime,attrs = Transition._nodeAttrs[uid],attr = attrs[name],anim = (attr) ? attr.transition : null,data,config;if (anim) {anim.removeProperty(name);anim._endNative(name);config = anim._config[name];data = {type: 'propertyEnd',propertyName: name,elapsedTime: elapsed,config: config};if (config && config.on && config.on.end) {config.on.end.call(Y.one(node), data);}if (anim._count <= 0) { // after propertyEnd firesanim._end(elapsed);node.style[TRANSITION_PROPERTY_CAMEL] = ''; // clean up style}}},destroy: function() {var anim = this,node = anim._node;if (node) {node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false);anim._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, // in seconds, default is 0.5* easing: 'ease-out', // default is 'ease'* delay: '1', // delay start for 1 second, default is 0** height: '10px',* width: '10px',** opacity: { // per property* value: 0,* duration: 2,* delay: 2,* easing: 'ease-in'* }* });* </pre>* @for Node* @method transition* @param {Object} config An object containing one or more style properties, a duration and an easing.* @param {Function} callback A function to run after the transition has completed.* @chainable*/Y.Node.prototype.transition = function(name, config, callback) {vartransitionAttrs = Transition._nodeAttrs[Y.stamp(this._node)],anim = (transitionAttrs) ? transitionAttrs.transition || null : null,fxConfig,prop;if (typeof name === 'string') { // named effect, pull config from registryif (typeof config === 'function') {callback = config;config = null;}fxConfig = Transition.fx[name];if (config && typeof config === 'object') {config = Y.clone(config);for (prop in fxConfig) {if (fxConfig.hasOwnProperty(prop)) {if (! (prop in config)) {config[prop] = fxConfig[prop];}}}} else {config = fxConfig;}} else { // name is a config, config is a callback or undefinedcallback = config;config = name;}if (anim && !anim._running) {anim.init(this, config);} else {anim = new Transition(this._node, config);}anim.run(callback);return this;};Y.Node.prototype.show = function(name, config, callback) {this._show(); // show prior to transitionif (name && Y.Transition) {if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes defaultif (typeof config === 'function') {callback = config;config = name;}name = Transition.SHOW_TRANSITION;}this.transition(name, config, callback);}return this;};Y.NodeList.prototype.show = function(name, config, callback) {var nodes = this._nodes,i = 0,node;while ((node = nodes[i++])) {Y.one(node).show(name, config, callback);}return this;};var _wrapCallBack = function(anim, fn, callback) {return function() {if (fn) {fn.call(anim);}if (callback && typeof callback === 'function') {callback.apply(anim._node, arguments);}};};Y.Node.prototype.hide = function(name, config, callback) {if (name && Y.Transition) {if (typeof config === 'function') {callback = config;config = null;}callback = _wrapCallBack(this, this._hide, callback); // wrap with existing callbackif (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes defaultif (typeof config === 'function') {callback = config;config = name;}name = Transition.HIDE_TRANSITION;}this.transition(name, config, callback);} else {this._hide();}return this;};Y.NodeList.prototype.hide = function(name, config, callback) {var nodes = this._nodes,i = 0,node;while ((node = nodes[i++])) {Y.one(node).hide(name, config, callback);}return this;};/*** Animate one or more css properties to a given value. Requires the "transition" module.* <pre>example usage:* Y.all('.demo').transition({* duration: 1, // in seconds, default is 0.5* easing: 'ease-out', // default is 'ease'* delay: '1', // delay start for 1 second, default is 0** height: '10px',* width: '10px',** opacity: { // per property* value: 0,* duration: 2,* delay: 2,* easing: 'ease-in'* }* });* </pre>* @for NodeList* @method transition* @param {Object} config An object containing one or more style properties, a duration and an easing.* @param {Function} callback A function to run after the transition has completed. The callback fires* once per item in the NodeList.* @param {Boolean} callbackOnce If true, the callback will be called only after the* last transition has completed* @chainable*/Y.NodeList.prototype.transition = function(config, callback, callbackOnce) {var nodes = this._nodes,size = this.size(),i = 0,callbackOnce = callbackOnce === true,node;while ((node = nodes[i++])) {if (i < size && callbackOnce){Y.one(node).transition(config);} else {Y.one(node).transition(config, callback);}}return this;};Y.Node.prototype.toggleView = function(name, on, callback) {this._toggles = this._toggles || [];callback = arguments[arguments.length - 1];if (typeof name !== 'string') { // no transition, just toggleon = name;this._toggleView(on, callback); // call original _toggleView in Y.Nodereturn;}if (typeof on === 'function') { // Ignore "on" if used for callback argument.on = undefined;}if (typeof on === 'undefined' && name in this._toggles) { // reverse current toggleon = ! this._toggles[name];}on = (on) ? 1 : 0;if (on) {this._show();} else {callback = _wrapCallBack(this, this._hide, callback);}this._toggles[name] = on;this.transition(Y.Transition.toggles[name][on], callback);return this;};Y.NodeList.prototype.toggleView = function(name, on, callback) {var nodes = this._nodes,i = 0,node;while ((node = nodes[i++])) {node = Y.one(node);node.toggleView.apply(node, arguments);}return this;};Y.mix(Transition.fx, {fadeOut: {opacity: 0,duration: 0.5,easing: 'ease-out'},fadeIn: {opacity: 1,duration: 0.5,easing: 'ease-in'},sizeOut: {height: 0,width: 0,duration: 0.75,easing: 'ease-out'},sizeIn: {height: function(node) {return node.get('scrollHeight') + 'px';},width: function(node) {return node.get('scrollWidth') + 'px';},duration: 0.5,easing: 'ease-in',on: {start: function() {var overflow = this.getStyle('overflow');if (overflow !== 'hidden') { // enable scrollHeight/Widththis.setStyle('overflow', 'hidden');this._transitionOverflow = overflow;}},end: function() {if (this._transitionOverflow) { // revert overridden valuethis.setStyle('overflow', this._transitionOverflow);delete this._transitionOverflow;}}}}});Y.mix(Transition.toggles, {size: ['sizeOut', 'sizeIn'],fade: ['fadeOut', 'fadeIn']});}, '3.18.1', {"requires": ["node-style"]});