Bootstrap 自帶的 JavaScript 插件的動畫效果幾乎都是使用 CSS 過渡實現的,而其中的 transition.js 就是爲了判斷當前使用的瀏覽器是否支持 CSS 過渡。下面先來簡單瞭解下 CSS 過渡。html
CSS 過渡是指在 CSS 屬性發生改變時在一段時間內平滑地過渡,使用 CSS 僞類能夠很方便地使用:jquery
a { color: #333; transition: color 1s linear; } a:hover { color: #36f; }
這裏給連接設置了一個 1秒的顏色過渡效果,當鼠標通過連接激活連接 :hover
狀態的時候文字的顏色會從黑色平滑地變化爲藍色,鼠標移開時又平滑地變回來。web
僅僅依靠僞類來使用過渡顯得太單調了,使用 JavaScript 來動態添加刪除 class
才能盡情地玩弄過渡:bootstrap
/* 這是一個圓 */ .circle { background-color: red; border-radius: 50%; height: 100px; margin-left: 220px; transition: transform 1s linear; width: 100px; } /* 添加 left 類水平向左偏移 200 像素 */ .circle.left { transform: translateX(-200px); } /* 添加 right 類水平向右偏移 200 像素 */ .circle.right { transform: translateX(200px); }
JavaScript 的工做就是簡單的刪除和添加 class:api
function toLeft() { $('.circle').removeClass('right').addClass('left'); } function toRight() { $('.circle').removeClass('left').addClass('right'); }
你讓它往左,它不敢往右! demo 上面的例子展現了當元素狀態改變或者添加了某個 class
的時候過渡就開始發生了,那麼如何知道過渡何時結束呢?瀏覽器
想要知道過渡何時結束,就要監聽 transitionend
事件(事件名稱全是小寫字母)。ruby
$('.circle').one('transitionend', function() { alert('過渡結束啦!'); });
這裏使用 one
方法而不是 on
方法是爲了不 transitionend
事件屢次執行。 one
方法添加的事件回調只會執行一次,更多信息參考 官方 API 。app
不過這只是標準的事件名稱寫法,在標準以前瀏覽器有各自的實現方式以及不一樣的事件名稱(好比低版本的 Chrome 和 Safari 的該事件名稱就叫 webkitTransitionEnd
),因此爲了兼容更多的瀏覽器,一種比較笨拙的方式能夠寫成像下面這樣:ide
$('.circle').one('transitionend webkitTransitionEnd oTransitionEnd otransitionend', function() {});
很長一串,並且這種寫法還有一點問題!函數
在這裏 能夠看到爲何是上面這些事件名稱,而沒有 msTransitionEnd
之類。
爲了根據瀏覽器更有針對性地添加 transitionend
事件回調,而不是像上面那樣一骨碌地全加上,就須要先判斷一下該瀏覽器到底支持哪一種 transitionend
事件名稱,判斷方法則是根據瀏覽器支持 CSS 過渡的屬性名稱而定:
var el = document.createElement('bootstrap'); // 建立一個元素用於測試 if (el.style.transition !== undefined) { // 判斷元素的 style.transition 屬性若是存在 // 則以此類推該瀏覽器支持標準的 CSS transition 屬性以及標準的 transitionend 事件 } else if (el.style.WebkitTransition !== undefined) { // 判斷元素的 style.WebkitTransition 屬性若是存在 // 則該瀏覽器支持替代的 webkitTransitionEnd 事件 } else {...}
按照當前的主流瀏覽器趨勢總共須要判斷四種不一樣前綴的屬性名稱:
el.style.transition
el.style.WebkitTransition
el.style.MozTransition
el.style.OTransition
判斷方式就是這樣,廢話就說到這裏,直接上正牌代碼,Bootstrap transition.js 內部的判斷函數:
function transitionEnd() { // 建立一個元素用於測試 var el = document.createElement('bootstrap'); // 將全部主流瀏覽器實現方式整合成一個對象,用於遍歷 // key 是屬性名稱 // value 是事件名稱 var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' }; // 循環遍歷上面那個對象,判斷 CSS 屬性是否存在 for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] }; } } return false; }
執行該函數能夠獲得一個對象 {end: 'transitionend'}
或者 false
(表示瀏覽器不支持 CSS 過渡),該對象的 end
屬性保存着瀏覽器所支持的 transitionend
事件對應的名稱:
var transition = transitionEnd();
好比我使用低版本的 Chrome 瀏覽器的話,那麼獲得的對象就是 {end: 'webkitTransitionEnd'}
這樣;若是使用 IE 8 則是 false
,而後就能夠添加該事件的回調函數了:
// 若是 transition 爲 false 則不添加事件回調 transition && $('.circle').one(transition.end, function() {});
爲了與 jQuery 保持一致,將該返回結果賦值到 $.support.transition
上:
$.support.transition = transitionEnd(); // 使用方式是相似的 $.support.transition && $('.circle').one($.support.transition.end, function() {});
事件名稱的問題基本上解決了,可是這個事件有個問題就是有時根本不會觸發,這是由於屬性值沒有發生變化或沒有繪製行爲發生。要確保每次回調都會被調用,咱們增長一個定時器便可:
$.fn.emulateTransitionEnd = function(duration) { var called = false; // transitionend 事件是否已觸發標識 var $el = this; $(this).one($.support.transition.end, function () { called = true; // 表示已觸發 }); var callback = function() { if (!called) { $($el).trigger($.support.transition.end); // 未觸發,強制其觸發 } }; setTimeout(callback, duration); // 一段時間後檢測是否觸發 return this; };
該方法的做用是一段時間(就是過渡持續的時間 transition-duration
)事後若是 transitionend
事件沒有發生則強制在該元素上觸發這個事件。
$('.circle').one($.support.transition.end, function() {}); $('.circle').emulateTransitionEnd(1000); // 這個時間是過渡持續的時間
這樣確保過渡以後必定會有回調。到這裏,基本上就差很少了,不過 $.support.transition.end
好惡心啊!能不能像添加其它事件回調同樣使用事件名稱字符串的形式,好比 'click'
,固然能夠。
$(function () { $.support.transition = transitionEnd(); // 支持過渡的時候才執行後面的代碼 if (!$.support.transition) {return;} $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) { return e.handleObj.handler.apply(this, arguments); } } }; });
添加事件回調的時候就能夠像這樣:
$('.circle').one('bsTransitionEnd', function() {}) .emulateTransitionEnd(1000);
CSS 動畫一樣也有一個 animationend
事件,同時還有 animationstart
和 animationiteration
事件,能夠參考這種方式本身寫一個。