bootstrap大量使用了css的过渡实现动画,所以监听动画结束事件,并执行回调函数十分关键。浏览器对css动画的支持不同,有的不支持css过渡,有的即使支持过渡,但是不一定会触发动画结束事件,即使良好支持过渡的浏览器,不同的厂商,对应的结束事件类型不同,transition.js的作用就是:
1. 统一不同浏览器对css过渡结束事件的支持;
2. 即使浏览器不触发结束事件,也能保证回调函数执行。
transitionEnd():
function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) }
该段比较简单,比较陌生的是createElement('bootstrap')然后利用这个元素的style属性去判断是否支持动画结束事件属性的方式,原来html还可以这么玩。。为什么药这么做,我觉得可能的原因是这里仅仅是要判断浏览器的DOM是否支持某个DOM元素的属性,并不需要调整DOM结构,所以用这种自定义的元素的代价会比标准的html元素代价小。。。
emulateTransitionEnd:
$.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this }
这个函数添加到了jquery的原型上,这样每个jquery对象就都能调用了,它的作用是用来模拟动画结束事件,并在duration时间间隔后,调用回调函数。
关键代码一:
$(this).one('bsTransitionEnd', function () { called = true })
给当前的动画元素再绑定一次动画结束事件处理,假如浏览器支持,那么在bsTransitionEnd事件触发后,called将会变为true。
关键代码二:
var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration)
定义了一个计时器,在duration之后执行callback,callback里面判断,当called为false时,则人为触发bsTransitionEnd事件。假如浏览器支持动画结束事件,callback里面的人为触发就不会执行。
不过这里也有一个问题,就是这个duration的时间假如比动画运行的时间短的话,那么人为的触发就会先执行,不管浏览器支不支持css动画结束事件,所以要正确地使用这个组件,duration必须大于等于css transition中定义的时间,最后分析完剩余代码之后,可以看下bootstrap的组件是如何定义transition的时间跟duration的。
最后一部分代码:
$(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) } } })
关键代码一:
$.support.transition = transitionEnd()
这样就可以方便地判断当前是否支持过渡动画结束事件了。if($.support.transition) { ... }
关键代码二:
$.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) } }
这个暂时只能认为是固定的写法了,用来自定义事件,等将来学过jquery的源码后,自然就理解了吧。
最后,这个组件怎么用,bootstrap里面的每个组件都有例子,如alert.js中的close方法的最后:
$.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement()
首先用$.support.transition判断是否支持过渡结束事件,不支持的话,直接调用回调函数,否则对bsTransitionEnd绑定一次处理器,就是要执行的回调函数,同时为了保证在动画结束事件不触发时,依然能够执行回调函数,调用了emulateTransitionEnd函数。
最最后看下Alert.TRANSITION_DURATION跟css中的transition时间:
Alert.TRANSITION_DURATION = 150 .fade { opacity: 0; -webkit-transition: opacity .15s linear; -o-transition: opacity .15s linear; transition: opacity .15s linear; }
这两个时间是一样的!