• 关于CSS Transition,你需要知道的事


    css3的过渡属性,给web应用带来了简单优雅的动画,但是比起初次相见,他(transition)有许多细则。在这片文章中,我将会专研css3的过渡(transition)中更加复杂的部分,从链式和事件到硬件加速和动画函数。让浏览器控制动画序列,通过改变帧率,减少绘画和减少GPU的工作,能够优化性能和效率。

    应用 transition

    一个最简单使用transition的方法就是和CSS伪元素一起用,比如:hover。注意我们在指定属性名字,transition的时长,以及默计时函数,linear。

    .element {
      height: 100px;
      transition: height 2s linear;
    }
    
    .element:hover {
      height: 200px;
    } 

    当:hover伪元素被激活的时候,这高度会动态地在两秒内从100px过度到200px。

    duration是唯一在transition缩写中需要的项目。浏览器默认的定时方法是ease,以及一个all的属性,除非他们已经提供了。

    当谈论到激活transition,我们不希望被限制于使用伪元素 —— 很显然这不灵活。解决这个的方法就是用程序添加和删除class

    /* 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。

    过渡渐变

    不是所有的CSS属性都能过渡,最基本的规则是你只能过渡绝对值。比如,你不能让height从 0px过渡到auto,浏览器不能计算中间过度值,因此属性变化是瞬间的。Oli Studholme提供了便利的 一份完全过度属性的列表。

    同时,有一些很好的解决方法。第一个方法包括添加透明度到渐变,然后过渡到背景色。比如:

    .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 {  
       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;
       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 1cubic-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'}); 

    广州品牌设计公司https://www.houdianzi.com PPT模板下载大全https://redbox.wode007.com

    未来

    过渡(Transition)正在活跃地应用,以及下一个标准看上去很有前景。这个建议包括了一个新的javascript的API,这个api专注于过渡现存的限制,并给与开发者更多的灵活性。

    实际上,你可以在github上找到新API的铺垫。这包括了举例一个Animation构造函数,传递到一个元素,让它做动画,做动画的属性,还有其他的属性,诸如延迟。

    var anim = new Animation(elem, { left: '100px' }, 3);
    anim.play(); 

    有了这个新的API,你可以同步动画,提供私人定制的时间函数,并且或者完整的回调。这真是一件激动人心的事啊!

    到目前为止,你希望对CSS过渡有更深层次的了解,并且结合简单的API能够提供复杂丰富的效果。 大多数javascript例子都直接来自于GFX,一个jQueryCSS过渡库。同样在core library,我包含了一些的额外的特效,比如滑动进/出,外加进/出和3D翻转。

  • 相关阅读:
    L2-011 玩转二叉树 二叉树
    L2-010 排座位 并查集
    L2-009 抢红包
    VS 编译报错:意外的字符
    关于js的类型转换
    github相关操作总结
    关于时间的相关处理
    uniapp实现简单的动画效果(不使用dom操作)
    uniapp选择日期
    vue使用音频组件
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/14111399.html
Copyright © 2020-2023  润新知