CSS3的transition属性为web应用带来了简洁优雅的动画效果,但是和第一次见面相比,他(transition)多了很多细节。在本文中,我将研究CSS3转换的更复杂部分,从链接和事件到硬件加速和动画功能。让浏览器控制动画序列可以通过改变帧速率、减少绘画和减少GPU的工作来优化性能和效率。应用过渡使用过渡的最简单方法之一是使用CSS伪元素,例如:hover。请注意,我们正在指定属性名称、过渡持续时间和默认计时函数线性。.element{高度:100px;transition:height2slinear;}.element:hover{height:200px;}:hover伪元素激活后,高度会在两秒内从100px动态过渡到200px。持续时间是过渡缩写中唯一需要的项目。浏览器的默认计时方法是ease和all属性,除非它们已经提供。当谈到激活转换时,我们不想局限于使用伪元素——显然那不灵活。解决方法是使用程序增删class/*CSS*/.element{opacity:0.0;变换:比例(0.95)translate3d(0,100%,0);transition:transform400msease,opacity400msease;}.element.active{opacity:1.0;变换:比例(1.0)translate3d(0,0,0);}.element.inactive{不透明度:0.0;transform:scale(1)translate3d(0,0,0);}//带有jQ??uery的JSvaractive=function(){$('.element').removeClass('inactive').addClass('active');};varinactive=function(){$('.element').removeClass('active').addClass('inactive');};在上面的示例中,我们使用了2个不同的转换。激活时,元素向上滑动,不激活时,它会淡出。所有javascript所做的就是在活动类和非活动类之间切换。TransitionGradients并不是所有的CSS属性都可以过渡,最基本的规则是你只能过渡绝对值。例如,您不能将高度从0px转换为自动,并且浏览器无法计算中间转换值,因此属性更改是瞬时的。OliStudholme提供了一个方便的列表,列出了完全过顶的属性。与此同时,有一些很好的解决方法。第一种方法涉及向渐变添加透明度,然后过渡到背景颜色。例如:.panel{背景色:#000;背景图像:线性渐变(rgba(255、255、0、0.4),#FAFAFA);transition:background-color400msease;}.panel:hover{background-color:#DDD;}如果渐变是连续的,你可以过渡background-position,如此处所写,否则,你最后的办法是创建两个元素,一个在另一个之上,并转换你的透明度。.element{宽度:100px;高度:100px;位置:相对;背景:线性渐变(#C7D3DC,#5B798E);}.element.inner{内容:'';位置:绝对;左:0;顶部:0;右:0;底部:0;背景:线性渐变(#DDD,#FAFAFA);不透明度:0;transition:opacity1slinear;}.element:hover.inner{opacity:1;}后一种方法需要注意,这需要额外的标记,并且内部div能够捕获指针事件。像:before和:after这样的伪元素可能是一个过于理想的用例。左边距和边距等属性的硬件加速转换将导致浏览器每帧重新计算样式。这是非常昂贵的,并且会导致不必要的重绘,尤其是当屏幕上有很多元素时。这在手机等低性能设备上尤其明显。解决方案是使用CSS过渡来减少GPU的渲染压力。简单来说,这会在过渡期间将元素变成图像,避免任何样式重新计算,从而大大提高性能。强制浏览器使用硬件渲染元素的一种简单方法是设置转换的Z轴。为此,您可以使用translate3d:transform:translate3d(0,0,0);但这并不是性能的根本解决方案,而且会给自身带来很多问题。您应该只在需要时才使用硬件加速,根本不需要在每个元素上都使用它。例如,硬件加速会导致细微的字体问题,例如字体出现时会失去粗体。这是由于当元素被硬件加速时不支持子像素抗锯齿的错误。您可以看到两种渲染模式之间的明显区别。此外,不同的浏览器使用不同的硬件加速库,这可能会导致跨浏览器问题。例如,当Chrome和Safari都有WebKit内核时,Chrome使用Skia进行图形渲染,而Safari同时使用CoreGraphics。这两个库之间的差异是微妙的,但它们确实存在。您可以使用Chrome的开发人员工具来概览页面,显示所有重绘。另外,你可以在开发者工具选项中选择显示三角形,甚至可以通过about:flags打开复合渲染层边界,查看GPU应用了哪一层。关键是批量DOM刷新,减少绘画,减少GPU的最大压力。如果由于硬件加速导致浏览器之间出现显示问题,例如闪烁或抖动,请确保您没有在元素上使用transform3d()CSS属性。裁剪要充分利用GPU渲染,您需要避免CSS样式转换,而不是重新计算宽度等样式属性。如果您真的需要为元素的宽度设置动画怎么办?解决方案是剪辑。在下面的示例中,您可以看到一个搜索框和2个过渡状态。第二个展开状态被剪裁元素隐藏。要过渡这个扩展的宽度,我们需要做的就是将X轴向左移动。这里的关键是我们使用translate3d而不是改变元素的宽度。.clipped{溢出:隐藏;位置:相对;}.clipped.clip{右:0px;宽度:45px;高度: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个关键点来编写自己的时间函数。过渡:-webkit-transform1scubic-bezier(.17,.67,.69,1.33);规划过渡用CSS编写过渡很棒,但有时您需要更多控制,尤其是当涉及到链式过渡时。幸运的是,我们不仅可以从javascript调用转换,还可以定义它们。CSStransitions有一个神奇的all属性,它确保任何属性更改都是过渡的。让我们看看如何练习它们vardefaults={duration:400,easing:''};$.fn.transition=function(properties,options){options=$.extend({},defaults,options);properties['webkitTransition']='all'+options.duration+'ms'+options.easing;$(this).css(属性);};现在我们有了一个jQuery函数$.fn.transition,我们可以使用它以编程方式调用转换。$('.element').transition({background:'red'});过渡回调的下一步是链式过渡,是过渡结束后的回调。你可以在Webkit中通过监听webkitTransitionEnd事件来获得这个状态。varcallback=function(){//...}$(this).one('webkitTransitionEnd',callback)$(this).css(properties);请记住,有时事件不受约束,通常在那些属性没有变化或未触发绘画的情况下。为了确保我们总是有回调,让我们设置一个超时时间,它会为我们手动触发时间。$.fn.emulateTransitionEnd=function(duration){varcalled=false,$el=this;$(this).one('webkitTransitionEnd',function(){called=true;});varcallback=function(){if(!called)$($el).trigger('webkitTransitionEnd');};设置超时(回调,持续时间);};我们在设置元素css之前调用$.fn.emulateTransitionEnd()来确保我们的transition会有CSS回调。$(this).one('webkitTransitionEnd',callback);$(this).emulateTransitionEnd(options.duration+50);$(this).css(properties);链接转换现在我们可以引用Transitions在完成时获得回调,然后我们可以开始对转换进行排序。我们可以编写自己的序列来执行此操作,但由于我们使用的是jQuery,因此我们最好破解库的现有方法。jQuery提供了两个关键函数及其队列和API连接,$.fn.queue(callback)和$.fn.dequeue()。前者向队列添加回调,后者执行下一个排队项。换句话说,我们需要在$.fn.queue回调中设置我们的CSS转换,然后确保当转换完成时,我们调用$.fn.dequeuevar$el=$(this);$el.queue(function(){$el.one('webkitTransitionEnd',function(){$el.dequeue();});$el.css(properties);});这个例子相对简单,但它让我们创建复杂的,甚至可以使用jQuery的delay()函数:例如:$('.element').transition({left:'20px'}).delay(200).transition({背景:'红色'});在转换期间,您将需要两组CSS属性。initial属性是动画应该开始的地方,final属性是过渡应该结束的地方。$('.element').css({left:'10px'}).transition({left:'20px'});但是,您会发现,如果您应用两组属性,一组紧接着另一组运行,那么浏览器会尝试优化属性更改,忽略您的初始属性并阻止转换。场景之后,浏览器绘制之前,batch属性的变化往往会加快渲染速度,往往会产生不良反应。解决方案是在两组属性之间强制重绘。一个简单的方法是获取Dom元素的offsetHeight属性,就像这样$.fn.redraw=function(){$(this).each(function(){varredraw=this.offsetHeight;});};这个在所有浏览器都可以用,但是我碰巧在安卓上用过一次,这个还是不行。替代方法是超时或切换类名。$('.element').css({left:'10px'}).redraw().transition({left:'20px'});FutureTransition正在积极应用,下一个标准看起来很有前途。该提案包括一个新的javascriptAPI,该API专注于转换现有限制并为开发人员提供更大的灵活性。其实在github上就可以找到新API的伏笔。这包括实例化一个Animation构造函数,将其传递给要设置动画的元素、要设置动画的属性以及延迟等其他属性。varanim=newAnimation(elem,{left:'100px'},3);anim.play();使用这个新的API,您可以同步动画、提供自定义计时功能和/或完成回调。这真是一件令人兴奋的事情!
