V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
McContax
V2EX  ›  JavaScript

不懂就问,为什么 JS 要加入 setTimeout, css 的 transition 才能生效

  •  
  •   McContax · 2019-12-28 11:32:09 +08:00 · 5582 次点击
    这是一个创建于 1821 天前的主题,其中的信息可能已经有所发展或是发生改变。

    当然我的标题有点极端化了,以前使用 JS 修改一个 html 标签的样式,配合 css 的 transition 是可以看得到过渡的,然而最近的一个项目使用 transform 搭配 transition,标签的移动直接一步到位没有动画过渡效果,但是加入了 setTimeout 之后,transition 就能显现出来,下面是代码

    <-- ! HTML -->
    <div class="slideshow">
        <div class="carousel-inner">
            <div class="carousel-item active">
                <img src="http://8.8.8.8/webstorm/20191216headerimg01.jpg">
            </div>
            <div class="carousel-item">
                <img src="http://8.8.8.8/webstorm/20191210headerimg01.jpg">
            </div>
            <div class="carousel-item">
                <img src="http://8.8.8.8/webstorm/20191129headerimg01.jpg">
            </div>
        </div>
    </div>
    
    <-- ! CSS -->
    .carousel-inner {
        position: relative;
        width: 100%;
        overflow: hidden;
    }
    .carousel-item {
        position: relative;
        display: none;
        float: left;
        width: 100%;
        margin-right: -100%;
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
        transition: transform .6s ease-in-out;
    }
    .carousel-item-next {
        transform: translateX(100%);
    }
    .carousel-item-next, .carousel-item.active {
        display: block;
    }
    .carousel-item.active.to-left, {
        transform: translateX(-100%);
    }
    
    <-- ! JavaScript -->
    var nextPic = document.getElementsByClassName('active')[0].nextElementSibling;
    var currentPic = document.getElementsByClassName('active')[0];
    
    
    //将下一张图片的样式增加一个 carousel-item-next,使其 translateX(100%);display: block;并在轮播框外显示
    nextPic.setAttribute('class', 'carousel-item carousel-item-next');
    
    
    setTimeout(function () {
               /*给 active 状态的图片添加一个 to-left,使其 translateX(-100%),
                 carousel-item 本身已经设置了针对 transform 的 transition,
                 所以这一步完成就会显示往左移*/
               currentPic.setAttribute('class', 'carousel-item active to-left');
               
               /*给下一张轮播图设置成 translateX(0),从原来的 translateX(100%)到
                 translateX(0),配合 transition 实现左移进入轮播框*/
               nextPic.style.transform = 'translateX(0)';
    },0)
    setTimeout(function () {
               
               //下面这三条将动画完成后的样式重置,确定新的 active 轮播图
               currentPic.className = 'carousel-item';
               nextPic.className = 'carousel-item active';
               nextPic.style.transform = '';
    },2000)
    

    上面的代码是能够完成动画效果的,但是一开始写的时候我没有 setTimeout,没有 setTimeout 的情况下过渡效果没有了,直接显示过渡完之后的状态

    所以 setTimeout 跟 transition 究竟是个什么关系,setTimeout 是时间结束之后执行代码,又不是指定每一行代码执行的时间间隔

    还有,这个轮播思路是从 bootstrap 上面摸过来的,甚至连 class 名都没改

    12 条回复    2019-12-28 21:07:04 +08:00
    randyo
        1
    randyo  
       2019-12-28 11:43:34 +08:00 via Android   ❤️ 1
    不加的话就是执行完所有的 js 然后才渲染页面,这时候就是渲染的最终效果
    momocraft
        2
    momocraft  
       2019-12-28 12:08:56 +08:00
    因爲不 setTimeout 時沒有 reflow 過一次?
    pinews
        3
    pinews  
       2019-12-28 13:39:13 +08:00
    被浏览器“优化”了吧,我也遇到过。不过,你的这个要求应该用动画而不是转换。
    CAze
        4
    CAze  
       2019-12-28 14:00:15 +08:00
    把要执行的 js 代码写到 window.onload 函数里
    wanguorui123
        5
    wanguorui123  
       2019-12-28 14:23:33 +08:00
    setTimeout 目的是将当前函数放到事件队列的最末尾排队执行,页面渲染相对 setTimeout 中的方法会先被执行,这时候在添加 css 就会生效。不然页面没有渲染完成,直接添加 css 会失效的
    zlgodpig
        6
    zlgodpig  
       2019-12-28 15:01:06 +08:00 via Android
    display none 直接到 block(或其他),是不会触发动画的
    Austaras
        7
    Austaras  
       2019-12-28 15:38:44 +08:00
    加之前
    主任务->渲染
    加之后
    主任务->渲染->定时器任务->渲染
    顺带建议用 requestAnimationFrame
    miniwade514
        8
    miniwade514  
       2019-12-28 16:13:19 +08:00 via iPhone
    同步 JS 是阻塞渲染的,你看到的是所有同步代码执行完了的最终状态
    isukkaw
        9
    isukkaw  
       2019-12-28 16:29:15 +08:00   ❤️ 1
    帮忙把 #1 @randyo 没说完的话说完。
    setTimeout 会把操作推迟到 Event Loop 的任务队列中,等待主调用栈清空后再执行。因此浏览器在解析到 setTimeou 时,会先跳过这一段 JS 开始渲染页面。

    再扯远一点。使用 setTimeout 0 可以使耗时操作不再阻碍页面渲染、改善 FP/FCP。饿了么 H5 页面就把 Vue 扔进 setTimeout 0 里,让骨架屏的渲染不被 Vue 阻碍。

    如果你担心有的浏览器会煞有介事的把 setTimeout 0 优化掉,那你可以 setTimeout 1 或者 setTimeout 10
    muyunyun
        10
    muyunyun  
       2019-12-28 17:11:58 +08:00
    @zlgodpig display: none 到 block 不会触发动画是正解。动画可以分为两种形式的, 一种是 css 动画, 另一种是结合 JavaScript 动画库完成的动画, JavaScript 动画库本质做了事就是初始化了一些中间态 css 属性, 比如 width、height, 之前原来打算写一篇相关文章的, 一直没有时间。
    McContax
        11
    McContax  
    OP
       2019-12-28 20:10:45 +08:00
    @zlgodpig
    @muyunyun display 那里是没有要做动画的,我的思路是先将要移动的 carousel-item 移到轮播框的外边的右边 transform: translateX(100%);然后显示出来 display: block;,接下来移动的时候利用 transform translateX 改变位置做到动画效果,所以 display 那里本来就没做动画


    @isukkaw 你说对了,我发现 settimeout 如果设置为 0 的话,我的安卓魅蓝 2 自带浏览器会忽略掉直接渲染结果出来

    感谢上面回答的各位
    Tokin
        12
    Tokin  
       2019-12-28 21:07:04 +08:00
    display:none;
    会忽略动画,直接就隐藏了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2762 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 09:34 · PVG 17:34 · LAX 01:34 · JFK 04:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.