javascript - 移动 chrome 上的动画滞后

标签 javascript css mobile css-animations css-transitions

我正在尝试在我的网站上制作动画。我正在使用下面链接的类似版本的 jsfiddle 代码。在桌面上查看时,动画效果很好。然而,当在移动设备上查看时,特别是在我的 chrome 浏览器上,会有一个奇怪的延迟。当我在手机上打开 jsfiddle 时,它​​会显示完全相同的延迟。如果我重新启动 chrome 应用程序,延迟会很快消失。
Safari 中不会出现此问题。
我有最新的带有 IOS 14.6 和 chrome V90 的 iPhone。
https://jsfiddle.net/brodriguez98/e2bvwcja/33/
HTML:

<html>
 <p style = 'margin-top: 100vh;'>above</p>
 
 <img class = 'balltest show-on-scroll standard-push' src = 'http://www.pngall.com/wp-content/uploads/5/Sports-Ball-Transparent.png'/>
 
 <img class = 'balltest show-on-scroll fade-in' src = 'http://www.pngall.com/wp-content/uploads/5/Sports-Ball-Transparent.png'/>
  
 <p style = 'margin-bottom: 100vh'>below</p>
</html>
CSS:
.balltest {
    width: 50px;
}

.fade-in {
    opacity: 0;
    -webkit-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
    -moz-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
    -o-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
    transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 0.3s 0.25s ease-out;
    will-change: transform, opacity;
}

.standard-push {
    opacity: 0;
    transform: translateY(4em);
    -webkit-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out, translateZ(0);
    -moz-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
    -o-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
    transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 0.3s 0.25s ease-out;
    will-change: transform, opacity;
}

.is-visible {
    transform: translateY(0);
    opacity: 1;
}
Javascript:
var elementsToShow = document.querySelectorAll('.show-on-scroll');
$(window).scroll(function() {
    Array.prototype.forEach.call(elementsToShow, function (element) {
        if (isElementInViewport(element)) {
            element.classList.add('is-visible');
        } else {
            element.classList.remove('is-visible');
        }
    });
});


// Helper function from: http://stackoverflow.com/a/7557433/274826
function isElementInViewport(el) {
    // special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }
    var rect = el.getBoundingClientRect();
    return (
        (rect.top <= 0 &&
            rect.bottom >= 0) ||
        (rect.bottom >= (window.innerHeight || document.documentElement.clientHeight) &&
            rect.top <= (window.innerHeight || document.documentElement.clientHeight)) ||
        (rect.top >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight))
    );
}
我为小屏幕道歉,无法在我的手机上全屏显示 JSfiddle:
动画在重新启动移动 chrome 后立即工作:
https://www.loom.com/share/ac6c843b90d2428bb875572d55e32959
动画很快中断(当我关闭/重新加载页面时):
https://www.loom.com/share/e51cf88aa1a74aed8e4d1ed253e83ea0
这与我在使用移动 chrome 浏览器的网站上看到的行为完全相同。
更新:
下面的答案都不适合我。我忘了提到这种行为也发生在文本中。另外,感谢您建议代码和框,我 fork 了您的代码并通过删除图像使其更加简单,但我仍然在我的 iphone chrome 浏览器上得到相同的结果。我还尝试使用 onload 函数包装所有内容,但这也不起作用。
现在我可以用 JQuery 动画解决这个问题,但我仍然希望 CSS3 过渡在我的网站上工作。
https://codesandbox.io/s/animation-test-forked-tqurn?file=/index.html
enter image description here

最佳答案

加载页面时,这看起来像是“竞争条件”问题。
JS 在 IMG 请求完成之前运行。
要了解问题,有必要了解加载顺序:

  • 在加载/重新加载时,服务器响应文档 (*.html) 文件
  • 浏览器开始解析响应 (*.html) 并为找到的每个资源启动新请求:
  • CSS
  • JS
  • IMG

  • 这些请求在 中完成。不可预测命令。例如。大图像的加载时间可能比 *.css 文件长,...某些资源可能已经被浏览器缓存并且根本不会启动请求,...
    如果对 *.js 文件的请求完成 之前 IMG 请求完成,没有呈现 height找到该图像和新添加的 CSS 类 is-visible将启动 transition反正...
  • 一旦 IMG 请求完成(img 被渲染)。一个 Content Reflow为 IMG 触发。
    一个 正在进行的过渡在需要重绘的元素(IMG)上是“重置”并从关键帧 0 开始。
    这可以解释你的问题。

  • 这里有 3 个选项可以解决您的问题:
    A. 保留图像的最终尺寸。
  • 设置修复 height在 CSS 中并在 html 中添加类:
    .myImg {
      width: 50px;
      height: 50px;
    }
    
  • 您也可以添加 widthheight作为html属性。即使 *.css 仍在加载,最终尺寸现在也可以在 JS 中使用...
    <img height="50" width="50" class="..." src="...">
    

  • B. 为图像添加一些“加载检测”,并在图像完全加载之前阻止过渡。
  • 我们检查 img 是否已经加载:src设置和height被检测到-
  • 否则设置 onload该图像的事件(因为尚未加载)
  • 可选:您可以对该图像使用延迟加载,并且仅“按需”加载图像(参见最后一个示例)。 img 的 src设置为 data-src属性和和 src一旦图像可用,将由 JS 设置。

  • 现在我们可以使用 isLoaded(element)排除.scroll()中的图像的功能当前未满载的。
    这里是 jsFiddle ,或展开下面的示例...
    var elementsToShow = document.querySelectorAll('.show-on-scroll');
    $(window).scroll(function() {
        Array.prototype.forEach.call(elementsToShow, function (element) {
            if (isLoaded(element) && isElementInViewport(element)) {
                element.classList.add('is-visible');
            } else {
                element.classList.remove('is-visible');
            }
        });
    });
    
    [...elementsToShow].forEach((imgEl, i) => {
        if (
        imgEl.src &&
        imgEl.getBoundingClientRect().height
      ) {
        imgEl.dataset.isLoaded = true;
        console.log(`Img ${i} already loaded`);
      } else {
        console.log(`Img ${i} still loading... or should be lazyloaded`);
    
        imgEl.onload = function(e) {
          console.log(`Img ${i} finally loaded! onload event`);
            e.target.dataset.isLoaded = true;
            };
    
        if (imgEl.dataset.src) {
          console.log(`Img ${i} start lazy load...`);
            imgEl.src = imgEl.dataset.src;
        }
      }
    })
    
    function isLoaded(el) {
        return el.dataset.isLoaded
    }
    

    var elementsToShow = document.querySelectorAll('.show-on-scroll');
    $(window).scroll(function() {
      Array.prototype.forEach.call(elementsToShow, function(element) {
        if (isLoaded(element) && isElementInViewport(element)) {
          element.classList.add('is-visible');
        } else {
          element.classList.remove('is-visible');
        }
      });
    });
    
    [...elementsToShow].forEach((imgEl, i) => {
      if (
        imgEl.src &&
        imgEl.getBoundingClientRect().height
      ) {
        imgEl.dataset.isLoaded = true;
        console.log(`Img ${i} already loaded`);
      } else {
        console.log(`Img ${i} still loading... or should be lazyloaded`);
        
        imgEl.onload = function(e) {
          console.log(`Img ${i} finally loaded! onload event`);
          e.target.dataset.isLoaded = true;
        };
        
        if (imgEl.dataset.src) {
          console.log(`Img ${i} start lazy load...`);
          imgEl.src = imgEl.dataset.src;
        }
      }
    });
    
    function isLoaded(el) {
      return el.dataset.isLoaded
    }
    
    // Helper function from: http://stackoverflow.com/a/7557433/274826
    function isElementInViewport(el) {
      // special bonus for those using jQuery
      if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
      }
      var rect = el.getBoundingClientRect();
      return (
        (rect.top <= 0 &&
          rect.bottom >= 0) ||
        (rect.bottom >= (window.innerHeight || document.documentElement.clientHeight) &&
          rect.top <= (window.innerHeight || document.documentElement.clientHeight)) ||
        (rect.top >= 0 &&
          rect.bottom <= (window.innerHeight || document.documentElement.clientHeight))
      );
    }
    .balltest {
      width: 50px;
    }
    
    .fade-in {
      opacity: 0;
      -webkit-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
      -moz-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
      -o-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
      transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 0.3s 0.25s ease-out;
      will-change: transform, opacity;
    }
    
    .standard-push {
      opacity: 0;
      transform: translateY(4em);
      -webkit-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out, translateZ(0);
      -moz-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
      -o-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;
      transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 0.3s 0.25s ease-out;
      will-change: transform, opacity;
    }
    
    .is-visible {
      transform: translateY(0);
      opacity: 1;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <html>
    <p style='margin-top: 100vh;'>above</p>
    
    <img class='balltest show-on-scroll standard-push' src='http://www.pngall.com/wp-content/uploads/5/Sports-Ball-Transparent.png' />
    <img class='balltest show-on-scroll fade-in' src='http://www.pngall.com/wp-content/uploads/5/Sports-Ball-Transparent.png' />
    
    <img class='balltest show-on-scroll standard-push' data-src='http://www.pngall.com/wp-content/uploads/5/Sports-Ball-Transparent.png' />
    <img class='balltest show-on-scroll fade-in' data-src='http://www.pngall.com/wp-content/uploads/5/Sports-Ball-Transparent.png' />
    
    <p style='margin-bottom: 100vh'>below</p>
    
    </html>

    C. 等待 load文件事件
    您可以将您的 JS 初始化代码包装成 load整个文档的事件。在所有资源(CSS、IMG、..)完全加载后触发该事件。
    window.addEventListener('load', (event) => {
        // JS init code hier (images are loaded at this point!)
    });
    

    关于javascript - 移动 chrome 上的动画滞后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68385134/

    相关文章:

    javascript - ESTree 中使用什么语言来描述抽象语法树?

    javascript - 访问Appery.io中外部资源的内容

    javascript - 使用 new window[] 创建静态内部函数对象

    css - 为什么 "display: table-cell"坏了 "position: absolute"

    html - 使用 float 时出现 blockquote 问题

    css - css 中的手持媒体​​类型与支持移动站点的另一个域或子域?

    javascript - 上传 CSV,然后在 JavaScript 中作为数组输出

    html - 如何在不输入值的情况下将 CSS 添加到 Google Chart 的表格

    android - 在 Blu R1 HD(亚马逊手机)上启用 DEBUG 日志记录

    html - 封面不在 Safari 上呈现