源碼注釋
// Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. ;(function($, undefined){ var prefix = '', eventPrefix, // prefix瀏覽器前綴 -webkit等,eventPrefix事件前綴 vendors = { Webkit: 'webkit', Moz: '', O: 'o' }, //前綴數據源 不包含IE testEl = document.createElement('div'), //臨時DIV容器 supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, //變形檢測 transform, //變形 transitionProperty, transitionDuration, transitionTiming, transitionDelay,//過渡 animationName, animationDuration, animationTiming, animationDelay, //動畫 cssReset = {} //將駝峰字符串轉成css屬性,如aB-->a-b function dasherize(str) { return str.replace(/([a-z])([A-Z])/, '$1-$2').toLowerCase() } //修正事件名 function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase() } /** * 根據瀏覽器內核,設置CSS前綴,事件前綴 * 如-webkit, css:-webkit- event:webkit * 這里會在vendors存儲webkit,moz,o三種前綴 */ $.each(vendors, function(vendor, event){ if (testEl.style[vendor + 'TransitionProperty'] !== undefined) { prefix = '-' + vendor.toLowerCase() + '-' eventPrefix = event return false } }) transform = prefix + 'transform' //變形 //過渡 cssReset[transitionProperty = prefix + 'transition-property'] = cssReset[transitionDuration = prefix + 'transition-duration'] = cssReset[transitionDelay = prefix + 'transition-delay'] = cssReset[transitionTiming = prefix + 'transition-timing-function'] = cssReset[animationName = prefix + 'animation-name'] = cssReset[animationDuration = prefix + 'animation-duration'] = cssReset[animationDelay = prefix + 'animation-delay'] = cssReset[animationTiming = prefix + 'animation-timing-function'] = '' /** * 動畫常量數據源 * @type {{off: boolean, speeds: {_default: number, fast: number, slow: number}, cssPrefix: string, transitionEnd: *, animationEnd: *}} */ $.fx = { off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), //能力檢測是否支持動畫,具體檢測是否支持過渡,支持過渡事件 speeds: { _default: 400, fast: 200, slow: 600 }, cssPrefix: prefix, //css 前綴 如-webkit- transitionEnd: normalizeEvent('TransitionEnd'), //過渡結束事件 animationEnd: normalizeEvent('AnimationEnd') //動畫播放結束事件 } /** * 創建自定義動畫 * @param properties 樣式集 * @param duration 持續事件 * @param ease 速率 * @param callback 完成時的回調 * @param delay 動畫延遲 * @returns {*} */ $.fn.animate = function(properties, duration, ease, callback, delay){ //參數修正,傳參為function(properties,callback) if ($.isFunction(duration)) callback = duration, ease = undefined, duration = undefined if ($.isFunction(ease)) //傳參為function(properties,duration,callback) callback = ease, ease = undefined if ($.isPlainObject(duration)) //傳參為function(properties,{}) ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration // duration 數字:持續時間 字符串:取speeds: { _default: 400, fast: 200, slow: 600 }對應數字 if (duration) duration = (typeof duration == 'number' ? duration : ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 //動畫持續時間默認值 if (delay) delay = parseFloat(delay) / 1000 //延遲時間,除以1000轉換成s return this.anim(properties, duration, ease, callback, delay) } /** * 動畫核心方法 * @param properties 樣式集 * @param duration 持續事件 * @param ease 速率 * @param callback 完成時的回調 * @param delay 動畫延遲 * @returns {*} */ $.fn.anim = function(properties, duration, ease, callback, delay){ var key, cssValues = {}, cssProperties, transforms = '', // transforms 變形 cssValues設置給DOM的樣式 that = this, wrappedCallback, endEvent = $.fx.transitionEnd, fired = false //修正持續時間 if (duration === undefined) duration = $.fx.speeds._default / 1000 if (delay === undefined) delay = 0 //如果瀏覽器不支持動畫,持續時間設為0,直接跳動畫結束 if ($.fx.off) duration = 0 // properties是動畫名 if (typeof properties == 'string') { // keyframe animation cssValues[animationName] = properties cssValues[animationDuration] = duration + 's' cssValues[animationDelay] = delay + 's' cssValues[animationTiming] = (ease || 'linear') endEvent = $.fx.animationEnd //動畫結束事件 } else { //properties 是樣式集 cssProperties = [] // CSS transitions for (key in properties) //supportedTransforms.test(key) 正則檢測是否為變形 //key + '(' + properties[key] + ') ' 拼湊成變形方法 if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') ' else cssValues[key] = properties[key], cssProperties.push(dasherize(key)) //變形統一存入 cssValues cssProperties if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) // duration > 0可以播放動畫,且properties是對象,表明為過渡,上面有字符串,則為animate if (duration > 0 && typeof properties === 'object') { cssValues[transitionProperty] = cssProperties.join(', ') cssValues[transitionDuration] = duration + 's' cssValues[transitionDelay] = delay + 's' cssValues[transitionTiming] = (ease || 'linear') //默認線性速率 } } //動畫完成后的響應函數 wrappedCallback = function(event){ if (typeof event !== 'undefined') { if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below" $(event.target).unbind(endEvent, wrappedCallback) } else $(this).unbind(endEvent, wrappedCallback) // triggered by setTimeout fired = true $(this).css(cssReset) callback && callback.call(this) } //處理動畫結束事件 if (duration > 0){ //綁定動畫結束事件 this.bind(endEvent, wrappedCallback) // transitionEnd is not always firing on older Android phones // so make sure it gets fired //延時ms后執行動畫,注意這里加了25ms,保持endEvent,動畫先執行完。 //綁定過事件還做延時處理,是transitionEnd在older Android phones不一定觸發 setTimeout(function(){ //如果觸發過,就不處理 if (fired) return wrappedCallback.call(that) }, ((duration + delay) * 1000) + 25) } // trigger page reflow so new elements can animate //主動觸發頁面回流,刷新DOM,讓接下來設置的動畫可以正確播放 //更改 offsetTop、offsetLeft、 offsetWidth、offsetHeight;scrollTop、scrollLeft、scrollWidth、scrollHeight;clientTop、clientLeft、clientWidth、clientHeight;getComputedStyle() 、currentStyle()。這些都會觸發回流。回流導致DOM重新渲染,平時要盡可能避免,但這里,為了動畫即時生效播放,則主動觸發回流,刷新DOM。 this.size() && this.get(0).clientLeft //設置樣式,啟動動畫 this.css(cssValues) // duration為0,即瀏覽器不支持動畫的情況,直接執行動畫結束,執行回調。 if (duration <= 0) setTimeout(function() { that.each(function(){ wrappedCallback.call(this) }) }, 0) return this } testEl = null //去掉不必要的數據存儲,便於垃圾回收 })(Zepto)
// Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. ;(function($, undefined){ var document = window.document, docElem = document.documentElement, origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle// origShow展示 origHide隱藏 origToggle展示隱藏開關 /** * 便捷動畫的核心方法 * @param el DOM * @param speed 持續時間 * @param opacity 不透明度 * @param scale 縮放 * @param callback 回調 * @returns {*} */ function anim(el, speed, opacity, scale, callback) { //修正參數 anim(el,callback) if (typeof speed == 'function' && !callback) callback = speed, speed = undefined var props = { opacity: opacity } if (scale) { props.scale = scale el.css($.fx.cssPrefix + 'transform-origin', '0 0') //設置變形原點 } return el.animate(props, speed, null, callback)//不支持速率變化, } /** * 底層方法:隱藏顯示的元素 * @param el * @param speed * @param scale * @param callback * @returns {*} */ function hide(el, speed, scale, callback) { //不透明度設為0,即完全透明,相當於隱藏元素。這里的原理是播放不透明-透明的過渡。 //具體代碼為 $(dom).animate({opacity: 0, '-webkit-transform-origin': '0px 0px 0px', '-webkit-transform': 'scale(0, 0)' },800) //設置了變形原點,縮放為0,它就會縮到左上角再透明 return anim(el, speed, 0, scale, function(){ origHide.call($(this)) //調用隱藏 callback && callback.call(this) }) } /** * 顯示 * @param speed 持續時間 * @param callback 回調函數 * @returns {*} */ $.fn.show = function(speed, callback) { origShow.call(this) //具體代碼為 $(dom).animate({opacity: 1, '-webkit-transform-origin': '0px 0px 0px', '-webkit-transform': 'scale(1, 1)' },800) //設置了變形原點,縮放為0,它就會縮到左上角再透明 if (speed === undefined) speed = 0 else this.css('opacity', 0) return anim(this, speed, 1, '1,1', callback) } /** * 隱藏 * @param speed 持續時間 * @param callback 回調函數 * @returns {*} */ $.fn.hide = function(speed, callback) { if (speed === undefined) return origHide.call(this) else return hide(this, speed, '0,0', callback) } /** * 顯示、隱藏開關 * @param speed 持續時間 * @param callback 回調函數 * @returns {*} */ $.fn.toggle = function(speed, callback) { if (speed === undefined || typeof speed == 'boolean') return origToggle.call(this, speed) else return this.each(function(){ var el = $(this) el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback) }) } /** * 淡入淡出 * 原理: $(dom).animate({opacity: 1/0, '-webkit-transform-origin': '0px 0px 0px'},800) * @param speed 持續時間 * @param opacity 不透明度 * @param callback 回調函數 * @returns {*} */ $.fn.fadeTo = function(speed, opacity, callback) { return anim(this, speed, opacity, null, callback) } /** * 淡入 * 原理: $(dom).animate({opacity: 1, '-webkit-transform-origin': '0px 0px 0px'},800) * @param speed 持續時間 * @param callback 回調函數 * @returns {*} */ $.fn.fadeIn = function(speed, callback) { var target = this.css('opacity') if (target > 0) this.css('opacity', 0) else target = 1 return origShow.call(this).fadeTo(speed, target, callback) } /** * 淡出 * 原理: $(dom).animate({opacity: 0, '-webkit-transform-origin': '0px 0px 0px'},800) * @param speed 持續時間 * @param callback 回調函數 * @returns {*} */ $.fn.fadeOut = function(speed, callback) { return hide(this, speed, null, callback) } /** * 淡入淡出開關 * @param speed 持續時間 * @param callback 回調函數 * @returns {*} */ $.fn.fadeToggle = function(speed, callback) { return this.each(function(){ var el = $(this) el[ (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut' ](speed, callback) }) } })(Zepto)
方法圖

