有关css重绘和回流的一个例子

最近做了一个导航条动画,鼠标hover的时候会有一个可移动的下划线,html结构大致如下

<div class="nav-sliderbar fl rel">
    <ul class="clearfix">
        <li class=""><a href="/b/AAA">AAA</a></li>
        <li class=""><a href="/b/BBB">BBB</a></li>
        <li class=""><a href="/b/CCC">CCC</a></li>
        <li class=""><a href="/b/DDD">DDDD</a></li>
    </ul>
    <span class="slider-underline"></span>
</div>

slider-underline使用了transition样式:

.slider-underline {
    position: absolute;
    border-width: 1px 0;
    border-style: solid;
    border-color: #444;
    height: 0px;
    bottom: 12px;
    width: 26px;
    left : -26px;
    -webkit-transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
       -moz-transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
         -o-transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
            transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
}

在页面生成的时候根据板块不同需要初始化slider-underline的位置。

var underline_width = $('.slider-underline').width();
var active_position = $('.nav-sliderbar .actived').length ? $('.nav-sliderbar .actived').width()/2 + $('.nav-sliderbar .actived').position().left - underline_width/2 : - underline_width;
//上面两句是计算的导航条滑块的初始的位置,正好在激活的板块对应位置的下方。
$('.slider-underline').css('left', active_position+'px');

由于slider-underline默认有transition,因此在页面刷新初始化其left位置的时候就会触发动画效果。这个体验不是很好,希望能在初始化的时候不触发这个动画,因此考虑了将动画效果做成一个类:

.underline-bezier {
    -webkit-transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
       -moz-transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
         -o-transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
            transition: left 0.2s cubic-bezier(0.16, 0.71, 0.46, 1.43);
}

js底下添加:

   $('.slider-underline').css('left', active_position+'px');
   $('.slider-underline').addClass('underline-bezier');

然而此处出现了问题,这么做了页面效果并没有变化。
按理说修改left会导致页面的重绘和回流,但是实际上却没有达到预想的效果。

翻阅一些资料后,个人得出的结论是,浏览器为了减少重绘的次数,会维护一个flush队列,当有需要重排的时候将操作放入队列,队列满时再一次性重绘。所以上面这两句被浏览器合并到一起写入了。

鉴于这个原因,我尝试出了两种解决办法:

  1. 使用setTimeout

    setTimeout(function() {
        $('.slider-underline').addClass('underline-bezier');
    },0);

    这个方法已开始写的时候只是尝试性的,但是达到了需要的效果,具体原因究竟是因为浏览器任务插入的问题还是因为执行其中函数前flush队列已满触发了重绘,还真正没有搞清楚。

  2. 强制提前flush队列
    当获取以下属性的时候,浏览器为了获取准确的位置会强制浏览器提前flush队列。

    1. offsetTop, offsetLeft, offsetWidth, offsetHeight

    2. scrollTop/Left/Width/Height

    3. clientTop/Left/Width/Height

    4. width,height

    5. getComputedStyle() / currentStyle

    因此将js改为:

    $('.slider-underline').css('left', active_position+'px');
    var get_left = $('.slider-underline').css('left');
    $('.slider-underline').addClass('underline-bezier');

    就能达到预想的效果了。

    原文作者:saigyouyou
    原文地址: https://segmentfault.com/a/1190000008915952
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞