如何修复 Chrome 中的页面滚动延迟

Posted

技术标签:

【中文标题】如何修复 Chrome 中的页面滚动延迟【英文标题】:How to fix page scroll lag in Chrome 【发布时间】:2019-06-12 12:46:50 【问题描述】:

我一直在做一个新的静态网站,这是一个非常简单的构建,但有一些微妙的 CSS 动画,由 isInViewport js 函数触发。

我注意到 Chrome 在滚动页面时会出现少量延迟,这非常不和谐。然而在 Safari 中,Firefox 绝对没问题。

这是我的js,用于在元素位于视口中时触发。

$.fn.isInViewport = function() 
  var elementTop = $(this).offset().top;
  var elementBottom = elementTop + $(this).outerHeight();

  var viewportTop = $(window).scrollTop();
  var viewportBottom = viewportTop + $(window).height() - 100;

  return elementBottom > viewportTop && elementTop < viewportBottom;
;

$(window).on('resize scroll', function() 
  setTimeout(function()
    $('section').each(function() 
      if ($(this).isInViewport()) 
        $(this).addClass('reveal');
      
      else 
      
    );
   , 300);
);

有什么不正常的事情我不应该做的吗?

这里是与动画相关的CSS

.image-reveal 
  position: relative;
  overflow: hidden;


.image-reveal-cover 
    background: #fcfcfc;
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    z-index: 5;
    transform: translateX(0);
    -webkit-transform: translateX(0);

    transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;
    -webkit-transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;


.image-reveal img 
  position: relative;
  z-index: 2;
  opacity: 0;
  transform: scale(1.4);
  -webkit-transform: scale(1.4);

  transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;
  -webkit-transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;


section.reveal .image-reveal-cover 
  transform: translateX(100%);
  -webkit-transform: translateX(100%);


section.reveal .image-reveal img 
  opacity: 1;
  transform: scale(1);
  -webkit-transform: scale(1);

这是一个codepen,显示了 Chrome 中滚动抖动的示例。

我是否错误地触发了我的动画?我认为使用变换作为 CSS 移动的基础相对没有延迟?

感谢任何建议。谢谢。


我也在Mac OS Mojave上使用Chrome 71.0.3578.98

【问题讨论】:

性能完全取决于浏览器的JS实现。您真正可以用您的逻辑做的唯一一件事就是停止多次重新创建相同的 jQuery 对象。如果这仍然不够高效,请使用纯 JS 而不是 jQuery。如果这仍然不起作用,那么您无能为力。 另外请注意,您的 Codepen 在 Chrome 中对我来说是流畅的,但这取决于运行浏览器的机器的能力——不过,每个浏览器也是如此。 我正在运行 4 GHz Intel Core i7 16GB RAM iMac。 Codepen 相对平滑,但您没有看到非常轻微的波动吗?它非常微妙,但与 Safari 相比令人沮丧。 这很奇怪,我在 Windows 10 上使用的是 i5 7600 @ 3.5Ghz,它像黄油一样光滑。我建议更改操作系统;) 哈哈,至少它可能不在我的手中,这让人放心。使用 on resize 是否存在潜在问题? 【参考方案1】:

对于这个例子,我有太多顾虑了;

摆脱 Jquery 它很慢,你可以用 JS 做任何事情 (http://youmightnotneedjquery.com/) 为什么超时?你不需要它。如果您要使用它,请终止其他超时,以免重复。 使用 ES6(易于理解、阅读和编码)您也可以编译到较低版本。它可以让您随时了解自己的职业道路。 不要为每个滚动运行选择器。在触发滚动之前使用选择器。

这里是例子:

class ScrollControl 
  constructor() 
    this.sectionDOMList = document.querySelectorAll('section');
    this.initListeners();
  

  initListeners() 
    window.addEventListener('scroll', (e) => 
      this.animateSections();
    );
  

  animateSections() 
    for (let i = 0; i < this.sectionDOMList.length; i++) 
      if (this.isInViewport(this.sectionDOMList[i])) 
      	if(!this.sectionDOMList[i].isReveal)
          this.sectionDOMList[i].isReveal = true;
          this.sectionDOMList[i].classList.add('reveal');
        
      
    
  

  isInViewport(element) 
    const elementCenter = (element.getBoundingClientRect().top + element.getBoundingClientRect().bottom)/2;
    const viewportTop = window.scrollY;
    const viewportBottom = viewportTop + window.innerHeight;
    return elementCenter < viewportBottom;
  


(function() 
  new ScrollControl();
)();
/* Reset */

html 
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-size: 16px;


*,
*:before,
*:after 
  -webkit-box-sizing: inherit;
  box-sizing: inherit;


body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
ol,
ul 
  margin: 0;
  padding: 0;
  font-weight: normal;


ol,
ul 
  list-style: none;


img 
  max-width: 100%;
  height: auto;


/* Base */

body 
  width: 100%;
  height: 100%;
  background: white;
  color: #3b3b3b;
  font-family: 'Larsseit', Helvetica, Arial, sans-serif;


.container 
  max-width: 1640px;
  margin-left: 30px;
  margin-right: 30px;


@media (min-width: 768px) 
  .container 
    margin-left: 80px;
    margin-right: 80px;
  


@media (min-width: 1800px) 
  .container 
    margin: 0 auto;
  


.vc 
  display: table;
  height: 100%;


.vc-ele 
  display: table-cell;
  vertical-align: middle;


section 
  padding: 400px 0;


section.full-vh 
  position: relative;
  width: 100%;
  height: 100vh;
  padding: 0;


.row 
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-wrap: wrap;
  flex-wrap: wrap;


.col-half 
  width: 100%;


@media (min-width: 992px) 
  .col-half 
    width: 50%;
  


.row.align-center 
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;


/* Typography */

h1 
  font-size: 52px;


h2 
  font-size: 26px;


p 
  font-family: 'Larsseit', Helvetica, Arial, sans-serif;
  font-size: 16px;
  font-weight: 300;
  line-height: 1.5;
  color: #3b3b3b;


/* Image reveal */

.image-reveal 
  position: relative;
  overflow: hidden;


.image-reveal-cover 
  background: #fcfcfc;
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  z-index: 5;
  transform: translateX(0);
  -webkit-transform: translateX(0);
  transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;
  -webkit-transition: transform cubic-bezier(.19, 1, .22, 1) 1.75s;


.image-reveal img 
  position: relative;
  z-index: 2;
  opacity: 0;
  transform: scale(1.4);
  -webkit-transform: scale(1.4);
  transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;
  -webkit-transition: all cubic-bezier(.19, 1, .22, 1) 1.5s;


section.reveal .image-reveal-cover 
  transform: translateX(100%);
  -webkit-transform: translateX(100%);


section.reveal .image-reveal img 
  opacity: 1;
  transform: scale(1);
  -webkit-transform: scale(1);
<div id="page">
  <div class="container">

    <section class="full-vh">
      <div class="vc">
        <div class="vc-ele">
          <h1>Scroll down</h1>
        </div>
      </div>
    </section>

    <section>
      <div class="row align-center">

        <div class="col-half">
          <div class="image-reveal">
            <div class="image-reveal-cover"></div>
            <img src="https://fearthewild.com/clients/playground/horse.jpg"  />
          </div>
        </div>

        <div class="col-half">
          <h2>This is my horse.</h2>
          <p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem
            quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.</p>
          <p>Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem
            ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?</p>
        </div>

      </div>
    </section>

  </div>
</div>

【讨论】:

如果您建议不要使用 jQuery,因为它很慢,您还应该建议不要在滚动事件上使用事件侦听器,因为这也很慢。如果您需要跟踪元素的可见性,请使用 Intersection Observer。 我很想看看 'one scroll listener, getBoundingClientRect(); 的基准测试。对于 1000 个元素'与'1000 个带有交叉观察者的元素':) @cloned 它仍然只有一个 IO,里面有上千个元素,就像一个滚动事件监听器一样。所以理论上它应该仍然表现更好。如果我们中的任何一个人感到无聊,我们应该尝试创建一个 jsfiddle 或其他东西来在实践中进行测试。【参考方案2】:

不要监听滚动(或调整大小)事件然后计算元素是否在视口中,而是使用Intersection Observer (IO) 来处理。

IO 观察一个(或多个)元素,并在它们相互或与视口相交时做出反应。

首先,您必须为您的 IO 指定选项:

let options = 
  rootMargin: '0px',
  threshold: 1.0


let observer = new IntersectionObserver(callback, options);

在您的情况下,您需要测试是否与窗口相交,因此不需要为选项指定 root 值。我们还指定每当触发 IO 时都会执行一个名为回调的函数。

下一步是定义应观察的元素,在您的情况下,这些元素将是部分元素:

let targets = document.querySelectorAll('section');
targets.forEach(target => 
  observer.observe(target);
);

现在一切都设置好了,剩下要做的就是定义回调函数中实际应该发生的事情:

let callback = (entries, observer) =>  
  entries.forEach(entry => 
    // Each entry describes an intersection change for one observed
    // target element
    // I think you are be interested in entry.isIntersecting check. 
  );
;

还有check this example 关于如何根据框在视口中的可见程度来更改背景颜色。

您也可以使用此polyfill from w3c 来支持旧版浏览器。

【讨论】:

请注意,IE 不支持 IO,如果您关心的话 这就是我在上一句中链接到polyifll的原因,这样你就可以在IE中使用IO了。它通过使用事件侦听器来滚动事件来重新创建 IO。虽然它的性能不如 IO,但它可以完成工作。【参考方案3】:

Chrome 滚动效果行为的延迟可能是由 CSS 引起的。如果您的 html 或 body 元素上有滚动行为规则,请尝试删除该规则。

删除此行: scroll-behavior: smooth

【讨论】:

以上是关于如何修复 Chrome 中的页面滚动延迟的主要内容,如果未能解决你的问题,请参考以下文章

页面延迟滚动不起作用,无法找出代码中的问题

Chrome 中的本机延迟加载

如何修复 Firefox 扩展弹出窗口中显示的滚动条

修复滚动时延迟的jquery淡入淡出

修复了容器div中的文本滚动

如何修复选项卡单击第一个输入屏幕滚动到顶部。仅在 Chrome 中