CSS3的过渡属性,给web应用带来了简单优雅的动画,可是比起初次相见,他(transition)有许多细则。javascript
在这片文章中,我将会专研CSS3的过渡(transition)中更加复杂的部分,从链式和事件到硬件加速和动画函数。php
让浏览器控制动画序列,经过改变帧率,减小绘画和减小GPU的工做,可以优化性能和效率。css
一个最简单使用transition的方法就是和CSS伪元素一块儿用,好比:hover。注意咱们在指定属性名字,transition的时长,以及默计时函数,linear。java
.element {
height: 100px;
transition: height 2s linear;
}
.element:hover {
height: 200px;
} 复制代码
当:hover伪元素被激活的时候,这高度会动态地在两秒内从100px过分到200px。git
duration是惟一在transition缩写中须要的项目。浏览器默认的定时方法是ease,以及一个all的属性,除非他们已经提供了。github
当谈论到激活transition,咱们不但愿被限制于使用伪元素 —— 很显然这不灵活。解决这个的方法就是用程序添加和删除classweb
/* CSS */
.element {
opacity: 0.0;
transform: scale(0.95) translate3d(0,100%,0);
transition: transform 400ms ease, opacity 400ms ease;
}
.element.active {
opacity: 1.0;
transform: scale(1.0) translate3d(0,0,0);
}
.element.inactive {
opacity: 0.0;
transform: scale(1) translate3d(0,0,0);
}
// JS with jQuery
var active = function(){
$('.element').removeClass('inactive').addClass('active');
};
var inactive = function(){
$('.element').removeClass('active').addClass('inactive');
}; 复制代码
以上的列子,咱们用了2个不一样的过渡(transition),当激活的时候,元素向上滑动,当无效化以后,淡出。全部的javascript所作的事就是切换active 和 inactive这两个class。typescript
不是全部的CSS属性都能过渡,最基本的规则是你只能过渡绝对值。好比,你不能让height从 0px过渡到auto,浏览器不能计算中间过分值,所以属性变化是瞬间的。Oli Studholme提供了便利的 一份彻底过分属性的列表。编程
同时,有一些很好的解决方法。第一个方法包括添加透明度到渐变,而后过渡到背景色。好比:api
.panel {
background-color: #000;
background-image: linear-gradient(rgba(255, 255, 0, 0.4), #FAFAFA);
transition: background-color 400ms ease;
}
.panel:hover {
background-color: #DDD;
} 复制代码
若是渐变是持续的,你能够过渡background-position,就像这里写的,不然,你的最后手段是建立两个元素,一个放在另外一个之上,而后过渡你的透明度。
.element {
width: 100px;
height: 100px;
position: relative;
background: linear-gradient(#C7D3DC,#5B798E);
}
.element .inner {
content: '';
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
background: linear-gradient(#DDD, #FAFAFA);
opacity: 0;
transition: opacity 1s linear;
}
.element:hover .inner {
opacity: 1;
} 复制代码
后者方法的须要注意的是,这须要额外的标记,而且在内部的div可以捕捉到指针事件。伪元素,相似:before和:after能够是过分理想的使用案例。
过渡某个属性,好比left和margin会致使浏览器每帧都会从新计算样式。这消耗至关昂贵,而且可能会致使没必要要的重绘,特别是若是你在屏幕上有不少元素。这在低性能设备上显得特别明显,好比手机。
这个解决方案是使用CSS过渡来减小渲染给GPU带来的压力。简单来讲,这在过渡的时候,将元素变成了一张图片,避免任何样式从新计算,这极大程度上增长了性能。一个简单强迫浏览器用硬件渲染一个元素的方法是,设置转型的Z轴,这个你能够用translate3d:
transform: translate3d(0,0,0);复制代码
不过这不是根治性能的方法,而且会带来许多自己的问题。只有当须要的时候,你才应该用硬件加速,而且彻底不须要在每一个元素上都使用它。
好比,硬件加速会致使微妙的字体问题,好比一个字体出现的时候失去了加粗效果。这是由于一个bug,当元素开启硬件加速的时候,不支持子像素抗锯齿。你能够看到在两个渲染模式下的一个清晰的差异。
此外,不一样的浏览器用不一样的硬件加速的库,这可能会形成跨浏览器问题。好比,当Chrome和Safari都是WebKit内核的,Chrome使用Skia来作图形渲染,然而同时Safari用的CoreGraphics。这两个库之间的差异是细微的,可是确是存在。
你能够用Chrome的开发者工具概览页面,显示全部的重绘。外加你能够在开发者工具选项中选择显示三角形,甚至能够经过 about:flags 打开复合渲染层边界,来看哪一个层是做用在GPU上的。关键是批量在DOM刷新下,减小绘画,而且从GPU上减小最多的的压力。
若是你在浏览器之间由于硬件加速而有了显示问题,好比闪烁或者颤动,确保你没有用transform3d()的CSS属性在元素上。
为了充分利用GPU渲染,你须要避免使用CSS样式变换而不是像width这种从新计算样式的属性。若果你确实须要给元素的宽度作动画?解决方案就是剪裁。
在如下的例子中个,你能够看到一个搜索框和2个过分状态。第二个扩展状态被一个剪裁的元素给隐藏了。
过渡这个扩展的宽度,咱们所须要作的就是转变X轴到左边。这边的关键是咱们用translate3d而不是改变元素的宽度。
.clipped {
overflow: hidden;
position: relative;
}
.clipped .clip {
right: 0px;
width: 45px;
height: 45px;
background: url(/images/clip.png) no-repeat
}
input:focus {
-webkit-transform: translate3d(-50px, 0, 0);
} 复制代码
确保咱们不会在每一帧从新计算元素的宽度,过渡会变得顺滑和高性能。
到目前为止,咱们用了一些浏览器预约义时间函数linear, ease, ease-in, ease-out和ease-in-out。对于更多复杂的时间函数来讲,咱们要写咱们本身的时间函数,经过定义贝塞尔曲线的4个关键点。
transition: -webkit-transform 1s cubic-bezier(.17,.67,.69,1.33);
复制代码
在CSS中写过渡很是好,可是有时候你须要更多控制,特别是谈到链式过渡的时候。幸运的是咱们不只能从javascript中调用过渡,也能定义他们。
CSS过渡有一个魔法般的all属性,这确保了任何属性改变都是过渡的。让咱们看看如何实践他们
var defaults = {
duration: 400,
easing: ''
};
$.fn.transition = function (properties, options) {
options = $.extend({}, defaults, options);
properties['webkitTransition'] = 'all ' + options.duration + 'ms ' + options.easing;
$(this).css(properties);
}; 复制代码
如今咱们有个jQuery函数$.fn.transition,咱们能够用它来编程调用过渡。
$('.element').transition({background: 'red'});复制代码
接下来的步奏是链式过渡,是过渡结束后回调。你能够在Webkit中得到这个状态,经过监听webkitTransitionEnd这个事件。
var callback = function () {
// ...
}
$(this).one('webkitTransitionEnd', callback)
$(this).css(properties); 复制代码
记住有时候事件没有绑定,常常是在那些属性不改变或者一个绘画没有被触发的状况下。为了确保咱们一直有一个回调,让咱们设置一个超时,这会手动帮咱们触发时间。
$.fn.emulateTransitionEnd = function(duration) {
var called = false, $el = this;
$(this).one('webkitTransitionEnd', function() { called = true; });
var callback = function() { if (!called) $($el).trigger('webkitTransitionEnd'); };
setTimeout(callback, duration);
}; 复制代码
咱们在设置元素css以前,请求 $.fn.emulateTransitionEnd(),以确保咱们过渡以后会有CSS回调。
$(this).one('webkitTransitionEnd', callback);
$(this).emulateTransitionEnd(options.duration + 50);
$(this).css(properties); 复制代码
所以,如今咱们可以经过写程引用过渡,当他们结束以后得到回调,咱们可以开始排序过渡。咱们可以写本身的序列来作这件事,可是咱们用了jQuery,咱们最好渗透库的现存方法。
jQuery提供了2个关键函数和他的队列和API联系,$.fn.queue(callback)和 $.fn.dequeue()。前者加了一个回调到队列中,而后后者执行下一个队列中的项目。
换句话说,咱们须要设置咱们的CSS过渡在$.fn.queue回调之中,而后确保当过渡完成的时候,咱们调用了$.fn.dequeue
var $el = $(this);
$el.queue(function(){
$el.one('webkitTransitionEnd', function(){
$el.dequeue();
});
$el.css(properties);
}); 复制代码
这个例子相对简单,可是他让咱们建立了复杂的链式动画,而且甚至使用jQuery的delay()函数:好比:
$('.element').transition({left: '20px'})
.delay(200)
.transition({background: 'red'}); 复制代码
在过渡的时候,你会须要两组CSS属性。最初的属性是动画应该从哪里开始,和最后的属性,过渡应该在哪里结束。
$('.element').css({left: '10px'})
.transition({left: '20px'}); 复制代码
然而,你会发现若是你应用了两组属性,一个立马在另外一个以后运行了,而后浏览器尝试优化属性改变,无视你的初始属性和阻止过渡。在场景以后,浏览器绘制以前,批处理属性变更,常常会加速渲染,常常会有不良反应。
解决方法是在两组属性之间强迫重绘。一个简单的方法是获取Dom元素的offsetHeight属性,就像这样
$.fn.redraw = function(){
$(this).each(function(){
var redraw = this.offsetHeight;
});
}; 复制代码
这在素有的浏览器中有有效,可是我有次很巧合地在Android中,这依然不行。可供替代的方法有timeout或者切换class名。
$('.element').css({left: '10px'})
.redraw()
.transition({left: '20px'}); 复制代码
过渡(Transition)正在活跃地应用,以及下一个标准看上去颇有前景。这个建议包括了一个新的javascript的API,这个api专一于过渡现存的限制,并给与开发者更多的灵活性。
实际上,你能够在github上找到新API的铺垫。这包括了举例一个Animation构造函数,传递到一个元素,让它作动画,作动画的属性,还有其余的属性,诸如延迟。
var anim = new Animation(elem, { left: '100px' }, 3);
anim.play(); 复制代码
有了这个新的API,你能够同步动画,提供私人定制的时间函数,而且或者完整的回调。这真是一件激动人心的事啊!