您的位置:首页 > Web前端 > BootStrap

Bootstrap transition.js 插件详解

2015-01-27 12:12 363 查看
Bootstrap 自带的 JavaScript 插件的动画效果几乎都是使用 CSS 过渡实现的,而其中的 transition.js 就是为了判断当前使用的浏览器是否支持 CSS 过渡。下面先来简单了解下 CSS 过渡。

CSS 过渡

CSS 过渡是指在 CSS 属性发生改变时在一段时间内平滑地过渡,使用 CSS 伪类可以很方便地使用:

a {
color: #333;
transition: color 1s linear;
}

a:hover {
color: #36f;
}

这里给链接设置了一个 1秒的颜色过渡效果,当鼠标经过链接激活链接
:hover
状态的时候文字的颜色会从黑色平滑地变化为蓝色,鼠标移开时又平滑地变回来。

仅仅依靠伪类来使用过渡显得太单调了,使用 JavaScript 来动态添加删除
class
才能尽情地玩弄过渡:

/* 这是一个圆 */
.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:

function toLeft() {
$('.circle').removeClass('right').addClass('left');
}

function toRight() {
$('.circle').removeClass('left').addClass('right');
}

你让它往左,它不敢往右!demo上面的例子展示了当元素状态改变或者添加了某个
class
的时候过渡就开始发生了,那么如何知道过渡什么时候结束呢?

Transitionend 事件

想要知道过渡什么时候结束,就要监听
transitionend
事件(事件名称全是小写字母)。

$('.circle').one('transitionend', function() {
alert('过渡结束啦!');
});

这里使用
one
方法而不是
on
方法是为了避免
transitionend
事件多次执行。
one
方法添加的事件回调只会执行一次,更多信息参考官方 API

不过这只是标准的事件名称写法,在标准之前浏览器有各自的实现方式以及不同的事件名称(比如低版本的 Chrome 和 Safari 的该事件名称就叫
webkitTransitionEnd
),所以为了兼容更多的浏览器,一种比较笨拙的方式可以写成像下面这样:

$('.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() {});

EmulateTransitionEnd

事件名称的问题基本上解决了,但是这个事件有个问题就是有时根本不会触发,这是因为属性值没有发生变化或没有绘制行为发生。要确保每次回调都会被调用,我们增加一个定时器即可:

$.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
事件,可以参考这种方式自己写一个。

参考资料

All you need to know about CSS Transitions

Using CSS transitions

Detecting CSS Animation Completion with JavaScript
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: