摘要:綁定過渡或動畫的結束事件綁定過渡或動畫的結束事件,在動畫結束時,執行處理過的回調函數。的回調執行比動畫時間長,目的是讓事件響應在之前,如果瀏覽器支持過渡或動畫事件,會在回調執行時設置成,的回調函數不會再重復執行。
fx 模塊為利用 CSS3 的過渡和動畫的屬性為 Zepto 提供了動畫的功能,在 fx 模塊中,只做了事件和樣式瀏覽器前綴的補全,沒有做太多的兼容。對于不支持 CSS3 過渡和動畫的, Zepto 的處理也相對簡單,動畫立即完成,馬上執行回調。
讀 Zepto 源碼系列文章已經放到了github上,歡迎star: reading-zepto
源碼版本本文閱讀的源碼為 zepto1.2.0
GitBook《reading-zepto》
內部方法 dasherizefunction dasherize(str) { return str.replace(/([A-Z])/g, "-$1").toLowerCase() }
這個方法是將駝峰式( camleCase )的寫法轉換成用 - 連接的連詞符的寫法( camle-case )。轉換的目的是讓寫法符合 css 的樣式規范。
normalizeEventfunction normalizeEvent(name) { return eventPrefix ? eventPrefix + name : name.toLowerCase() }
為事件名增加瀏覽器前綴。
為事件和樣式增加瀏覽器前綴 變量var prefix = "", eventPrefix, vendors = { Webkit: "webkit", Moz: "", O: "o" }, testEl = document.createElement("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 = {}
vendors 定義了瀏覽器的樣式前綴( key ) 和事件前綴 ( value ) 。
testEl 是為檢測瀏覽器前綴所創建的臨時節點。
cssReset 用來保存加完前綴后的樣式規則,用來過渡或動畫完成后重置樣式。
瀏覽器前綴檢測if (testEl.style.transform === undefined) $.each(vendors, function(vendor, event){ if (testEl.style[vendor + "TransitionProperty"] !== undefined) { prefix = "-" + vendor.toLowerCase() + "-" eventPrefix = event return false } })
檢測到瀏覽器不支持標準的 transform 屬性,則依次檢測加了不同瀏覽器前綴的 transitionProperty 屬性,直至找到合適的瀏覽器前綴,樣式前綴保存在 prefix 中, 事件前綴保存在 eventPrefix 中。
初始化樣式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"] = ""
獲取瀏覽器前綴后,為所有的 transition 和 animation 屬性加上對應的前綴,都初始化為 "",方便后面使用。
方法 $.fx$.fx = { off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), speeds: { _default: 400, fast: 200, slow: 600 }, cssPrefix: prefix, transitionEnd: normalizeEvent("TransitionEnd"), animationEnd: normalizeEvent("AnimationEnd") }
off: 表示瀏覽器是否支持過渡或動畫,如果既沒有瀏覽器前綴,也不支持標準的屬性,則判定該瀏覽器不支持動畫
speeds: 定義了三種動畫持續的時間, 默認為 400ms
cssPrefix: 樣式瀏覽器兼容前綴,即 prefix
transitionEnd: 過渡完成時觸發的事件,調用 normalizeEvent 事件加了瀏覽器前綴補全
animationEnd: 動畫完成時觸發的事件,同樣加了瀏覽器前綴補全
animate$.fn.animate = function(properties, duration, ease, callback, delay){ if ($.isFunction(duration)) callback = duration, ease = undefined, duration = undefined if ($.isFunction(ease)) callback = ease, ease = undefined if ($.isPlainObject(duration)) ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration if (duration) duration = (typeof duration == "number" ? duration : ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 if (delay) delay = parseFloat(delay) / 1000 return this.anim(properties, duration, ease, callback, delay) }
我們平時用得最多的是 animate 這個方法,但是這個方法最終調用的是 anim 這個方法,animate 這個方法相當靈活,因為它主要做的是參數修正的工作,做得參數適應 anim 的接口。
參數:properties:需要過渡的樣式對象,或者 animation 的名稱,只有這個參數是必傳的
duration: 過渡時間
ease: 緩動函數
callback: 過渡或者動畫完成后的回調函數
delay: 過渡或動畫延遲執行的時間
修正參數if ($.isFunction(duration)) callback = duration, ease = undefined, duration = undefined
這是處理傳參為 animate(properties, callback) 的情況。
if ($.isFunction(ease)) callback = ease, ease = undefined
這是處理 animate(properties, duration, callback) 的情況,此時 callback 在參數 ease 的位置
if ($.isPlainObject(duration)) ease = duration.easing, callback = duration.complete, delay = duration.delay, duration = duration.duration
這是處理 animate(properties, { duration: msec, easing: type, complete: fn }) 的情況。除了 properties ,后面的參數還可以寫在一個對象中傳入。
如果檢測到為對象的傳參方式,則將對應的值從對象中取出。
if (duration) duration = (typeof duration == "number" ? duration : ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000
如果過渡時間為數字,則直接采用,如果是 speeds 中指定的 key ,即 slow 、fast 甚至 _default ,則從 speeds 中取值,否則用 speends 的 _default 值。
因為在樣式中是用 s 取值,所以要將毫秒數除 1000。
if (delay) delay = parseFloat(delay) / 1000
也將延遲時間轉換為秒。
anim$.fn.anim = function(properties, duration, ease, callback, delay){ var key, cssValues = {}, cssProperties, transforms = "", that = this, wrappedCallback, endEvent = $.fx.transitionEnd, fired = false if (duration === undefined) duration = $.fx.speeds._default / 1000 if (delay === undefined) delay = 0 if ($.fx.off) duration = 0 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 { cssProperties = [] // CSS transitions for (key in properties) if (supportedTransforms.test(key)) transforms += key + "(" + properties[key] + ") " else cssValues[key] = properties[key], cssProperties.push(dasherize(key)) if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) 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 setTimeout(function(){ if (fired) return wrappedCallback.call(that) }, ((duration + delay) * 1000) + 25) } // trigger page reflow so new elements can animate this.size() && this.get(0).clientLeft this.css(cssValues) if (duration <= 0) setTimeout(function() { that.each(function(){ wrappedCallback.call(this) }) }, 0) return this }
animation 最終調用的是 anim 方法,Zepto 也將這個方法暴露了出去,其實我覺得只提供 animation 方法就可以了,這個方法完全可以作為私有的方法調用。
參數默認值if (duration === undefined) duration = $.fx.speeds._default / 1000 if (delay === undefined) delay = 0 if ($.fx.off) duration = 0
如果沒有傳遞持續時間 duration ,則默認為 $.fx.speends._default 的定義值 400ms ,這里需要轉換成 s 。
如果沒有傳遞 delay ,則默認不延遲,即 0 。
如果瀏覽器不支持過渡和動畫,則 duration 設置為 0 ,即沒有動畫,立即執行回調。
處理animation動畫參數if (typeof properties == "string") { // keyframe animation cssValues[animationName] = properties cssValues[animationDuration] = duration + "s" cssValues[animationDelay] = delay + "s" cssValues[animationTiming] = (ease || "linear") endEvent = $.fx.animationEnd }
如果 properties 為 string, 即 properties 為動畫名,則設置動畫對應的 css ,duration 和 delay 都加上了 s 的單位,默認的緩動函數為 linear 。
處理transition參數else { cssProperties = [] // CSS transitions for (key in properties) if (supportedTransforms.test(key)) transforms += key + "(" + properties[key] + ") " else cssValues[key] = properties[key], cssProperties.push(dasherize(key)) if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) if (duration > 0 && typeof properties === "object") { cssValues[transitionProperty] = cssProperties.join(", ") cssValues[transitionDuration] = duration + "s" cssValues[transitionDelay] = delay + "s" cssValues[transitionTiming] = (ease || "linear") } }
supportedTransforms 是用來檢測是否為 transform 的正則,如果是 transform ,則拼接成符合 transform 規則的字符串。
否則,直接將值存入 cssValues 中,將 css 的樣式名存入 cssProperties 中,并且調用了 dasherize 方法,使得 properties 的 css 樣式名( key )支持駝峰式的寫法。
if (transforms) cssValues[transform] = transforms, cssProperties.push(transform)
這段是檢測是否有 transform ,如果有,也將 transform 存入 cssValues 和 cssProperties 中。
接下來判斷動畫是否開啟,并且是否有過渡屬性,如果有,則設置對應的值。
回調函數的處理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) }
如果瀏覽器支持過渡或者動畫事件,則在動畫結束的時候,取消事件監聽,注意在 unbind 時,有個 event.target !== event.currentTarget 的判定,這是排除冒泡事件。
如果事件不存在時,直接取消對應元素上的事件監聽。
并且將狀態控制 fired 設置為 true ,表示回調已經執行。
動畫完成后,再將涉及過渡或動畫的樣式設置為空。
最后,調用傳遞進來的回調函數,整個動畫完成。
綁定過渡或動畫的結束事件if (duration > 0){ this.bind(endEvent, wrappedCallback) setTimeout(function(){ if (fired) return wrappedCallback.call(that) }, ((duration + delay) * 1000) + 25) }
綁定過渡或動畫的結束事件,在動畫結束時,執行處理過的回調函數。
注意這里有個 setTimeout ,是避免瀏覽器不支持過渡或動畫事件時,可以通過 setTimeout 執行回調。setTimeout 的回調執行比動畫時間長 25ms ,目的是讓事件響應在 setTimeout 之前,如果瀏覽器支持過渡或動畫事件, fired 會在回調執行時設置成 true, setTimeout 的回調函數不會再重復執行。
觸發頁面回流// trigger page reflow so new elements can animate this.size() && this.get(0).clientLeft this.css(cssValues)
這里用了點黑科技,讀取 clientLeft 屬性,觸發頁面的回流,使得動畫的樣式設置上去時可以立即執行。
具體可以這篇文章中的解釋:2014-02-07-hidden-documentation.md
過渡時間不大于零的回調處理if (duration <= 0) setTimeout(function() { that.each(function(){ wrappedCallback.call(this) }) }, 0)
duration 不大于零時,可以是參數設置錯誤,也可能是瀏覽器不支持過渡或動畫,就立即執行回調函數。
系列文章讀Zepto源碼之代碼結構
讀Zepto源碼之內部方法
讀Zepto源碼之工具函數
讀Zepto源碼之神奇的$
讀Zepto源碼之集合操作
讀Zepto源碼之集合元素查找
讀Zepto源碼之操作DOM
讀Zepto源碼之樣式操作
讀Zepto源碼之屬性操作
讀Zepto源碼之Event模塊
讀Zepto源碼之IE模塊
讀Zepto源碼之Callbacks模塊
讀Zepto源碼之Deferred模塊
讀Zepto源碼之Ajax模塊
讀Zepto源碼之Assets模塊
讀Zepto源碼之Selector模塊
讀Zepto源碼之Touch模塊
讀Zepto源碼之Gesture模塊
讀Zepto源碼之IOS3模塊
附文譯:怎樣處理 Safari 移動端對圖片資源的限制
參考一步一步DIY zepto庫,研究zepto源碼7--動畫模塊(fx fx_method)/)
How (not) to trigger a layout in WebKit
2014-02-07-hidden-documentation.md
License署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)
最后,所有文章都會同步發送到微信公眾號上,歡迎關注,歡迎提意見:
作者:對角另一面
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88915.html
摘要:所以模塊依賴于模塊,在引入前必須引入模塊。原有的方法分析見讀源碼之樣式操作方法首先調用原有的方法,將元素顯示出來,這是實現動畫的基本條件。如果沒有傳遞,或者為值,則表示不需要動畫,調用原有的方法即可。 fx 模塊提供了 animate 動畫方法,fx_methods 利用 animate 方法,提供一些常用的動畫方法。所以 fx_methods 模塊依賴于 fx 模塊,在引入 fx_m...
摘要:讀源碼系列文章已經放到了上,歡迎源碼版本本文閱讀的源碼為改寫原有的方法模塊改寫了以上這些方法,這些方法在調用的時候,會為返回的結果添加的屬性,用來保存原來的集合。方法的分析可以看讀源碼之模塊。 Stack 模塊為 Zepto 添加了 addSelf 和 end 方法。 讀 Zepto 源碼系列文章已經放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的...
摘要:模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數據,另一部分是觸發事件,提交表單。最終返回的結果是一個數組,每個數組項為包含和屬性的對象。否則手動綁定事件,如果沒有阻止瀏覽器的默認事件,則在第一個表單上觸發,提交表單。 Form 模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數據,另一部分是觸發 submit 事件,提交表單。 讀 Zepto 源碼系列文章已...
摘要:的模塊用來獲取節點中的屬性的數據,和儲存跟相關的數據。獲取節點指定的緩存值。如果存在,則刪除指定的數據,否則將緩存的數據全部刪除。為所有下級節點,如果為方法,則節點自身也是要被移除的,所以需要將自身也加入到節點中。 Zepto 的 Data 模塊用來獲取 DOM 節點中的 data-* 屬性的數據,和儲存跟 DOM 相關的數據。 讀 Zepto 源碼系列文章已經放到了github上,歡...
摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個判斷需要引入設備偵測模塊。然后是監測事件,根據這三個事件,可以組合出和事件。其中變量對象和模塊中的對象的作用差不多,可以先看看讀源碼之模塊對模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經放到了github上,歡...
閱讀 920·2021-11-16 11:45
閱讀 2126·2021-10-09 09:44
閱讀 1341·2019-08-30 14:03
閱讀 1127·2019-08-26 18:28
閱讀 3328·2019-08-26 13:50
閱讀 1715·2019-08-23 18:38
閱讀 3450·2019-08-23 18:22
閱讀 3589·2019-08-23 15:27