美文网首页
写一个文字水平滚动跑马灯效果

写一个文字水平滚动跑马灯效果

作者: 般犀 | 来源:发表于2019-04-27 11:35 被阅读0次

    最近的一个页面需要一个文字水平滚动的效果,效果如图所示:


    文字水平滚动.gif

    本来想在网上直接找代码一把梭,但发现现有代码都是用的 jQuery + animation,不符合项目的技术栈,就想干脆自己实现一个。最终用了18行代码实现了上面的效果。

    原理

    具体原理跟轮播图类似,把包裹文字的元素复制多一个出来,当第一个元素滚动到看不见的时候,把第一个元素重新插到尾部,因为两个元素是紧挨着的,第一个元素滚动到看不见的一瞬间,第二个元素也已经到了开始的位置,所以肉眼看不出来元素插入的过程。最终实现了滚动效果。

    实现

    HTML
    <div class="scrollX">
         <p class="scrollTxt">窗前明月光,疑是地上霜,举头望明月,低头思故乡</p>
    </div>
    

    第一步,拿到<p>标签并复制,插入到父容器中:

    const domScroll = document.querySelector('.scrollTxt');
    const cloneNode = document.querySelector('.scrollTxt').cloneNode(true);
    domScroll.parentNode.appendChild(cloneNode);
    

    此时的HTML标签就变成了:

    <div class="scrollX">
         <p class="scrollTxt">窗前明月光,疑是地上霜,举头望明月,低头思故乡</p>
         <p class="scrollTxt">窗前明月光,疑是地上霜,举头望明月,低头思故乡</p>
    </div>
    

    这样有两个问题:
    ①父元素随着子元素的增大而增大,需要给父元素一个固定的宽度,且隐藏溢出的部分;
    ②两个<p>标签不在同一行,需要让两个元素在同一行才有水平滚动的效果。

    针对第一个问题,给 scrollX一个固定宽度加 overflow: hidden就可以解决。
    第二个问题,我是用了 flex布局,在父元素定义 display: flex就让两个子元素在了同一个水平线上。最终的CSS:

    .scrollX {
       display: flex;
       height: 100%;
       width: 500px;
       overflow: hidden;
    
      p {
        height: 100%;
        lex-shrink: 0;
        padding-right: 20px;
        white-space: nowrap;
       }
     }
    
    让元素动起来

    让元素滚动有两种方法
    ①将元素设置为 position: absolute并控制元素的left属性,让元素往左移动。
    ②使用transform并控制translateX()让元素往左移动。

    没吃过猪肉也见过猪跑,没写过动画的人,也知道第二种方法的性能会比第一种要好。为什么好呢,我们放在最后面总结,先把动画实现起来~

    我们要让元素往左滚,那么要滚多远呢?就是元素本身的宽度,当元素滚动的距离超过自身宽度时,进行元素插入尾部的操作:

    const domScroll2 = document.querySelectorAll('.scrollTxt')[1];
    let width = domScroll2.getAttribute('width');
    const animation = () => {
      if (width > -100) {
        domScroll.style = `transform: translateX(${width}%)`;
        domScroll2.style = `transform: translateX(${width}%)`;
        width -= 1;
      } else {
        domScroll.parentNode.appendChild(domScroll);
        width = 0;
      }
      setTimeout(animation, 100);    // 使用setTimeout 嵌套代替 setInterval
    };
    animation();
    

    代码写到这里,动画就可以动起来了。

    这里有一个实现的细节是,setTimeout 的嵌套代替setInterval,原因是 setInterval 的运行机制并不是真实的“每隔XX秒后调用指定的函数”,因为setInterval并不会将指定函数需要执行的时间考虑在内。比如我们用setInterval每隔100ms调用一次函数A,如果A本身的执行需要95ms,那么A执行完,经过至少5ms就会马上被调用,所以为了保证每次执行都有一定的间隔,要使用 setTimeout代替setInterval

    在查资料的过程中,想起来还有requestAnimationFrame这个API,这个API相当于一个时间被固定为 浏览器绘制频率(一般为16ms一次) 的setTimeout,但它是setTimeout的改进版,浏览器会为requestAnimationFrame做优化,以每一次浏览器的重绘为间隔执行动画。而且,当浏览器不在当前 tab 的时候,requestAnimationFrame调用的动画会停止。会根据浏览器绘制频率调整刷新时机。

    GPU渲染导致的动画中字体模糊的问题

    一开始实现的时候考虑过用 transition 来实现运动效果,发现运动过程中字体会变得模糊,而且不仅是动画中的字体会模糊,连页面上的其他字体也变得模糊起来。在实现了动画后考虑过使用will-change优化动画,发现也存在同样的问题(区别是will-change只会导致运动的字体变模糊)。
    一番查找发现文字模糊是因为浏览器启用了GPU渲染,而GPU渲染不具备CPU渲染的次像素抗锯齿,导致了文字会出现模糊。

    参考文章:
    【翻译】:CSS 的 will-change 属性详解 - 前端 - 掘金
    CSS3动画那么强,requestAnimationFrame还有毛线用? « 张鑫旭-鑫空间-鑫生活
    你所不知道的setInterval | 晚晴幽草轩
    深入理解requestAnimationFrame - 最骚的就是你 - 博客园

    相关文章

      网友评论

          本文标题:写一个文字水平滚动跑马灯效果

          本文链接:https://www.haomeiwen.com/subject/umghnqtx.html