移动 Chrome 上的动画滞后

Ber*_*uez 5 javascript css mobile css-transitions css-animations

我正在尝试在我的网站上制作动画。我正在使用下面链接的类似版本的 jsfiddle 代码。在桌面上查看时,动画效果很好。然而,当在移动设备上查看时,特别是在我的 Chrome 浏览器上,出现了奇怪的延迟。当我在手机上打开 jsfiddle 时,它​​显示出完全相同的延迟。如果我重新启动 chrome 应用程序,延迟就会消失,很快就会恢复。

在 Safari 中不会出现此问题。

我有最新的 iPhone,运行 IOS 14.6 和 chrome V90。

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>
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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))
    );
}
Run Code Online (Sandbox Code Playgroud)

对于屏幕太小,我深表歉意,无法在我的手机上全屏显示 JSfiddle:

重新启动移动 Chrome 后动画立即工作: https://www.loom.com/share/ac6c843b90d2428bb875572d55e32959

动画很快就中断了(当我关闭/重新加载页面时): https://www.loom.com/share/e51cf88aa1a74aed8e4d1ed253e83ea0

这与我在使用移动 Chrome 浏览器的网站上看到的行为完全相同。

更新: 下面的答案都不适合我。我忘了提及这种行为也发生在文本上。另外,感谢您建议codesandbox,我分叉了您的代码,并通过删除图像使其变得更加简单,但我仍然在我的iphone chrome浏览器上得到相同的结果。我还尝试用 onload 函数包装所有内容,但这也不起作用。

目前我可以使用 JQuery 动画修复此问题,但我仍然希望 CSS3 过渡能够在我的网站上工作。

https://codesandbox.io/s/animation-test-forked-tqurn?file=/index.html

在此输入图像描述

Exo*_* 4D 1

这看起来像是加载页面时的“竞争条件”问题。\nJS 在 IMG 请求完成之前运行。

\n

要理解这个问题,有必要了解加载顺序:

\n
    \n
  1. 加载/重新加载时,服务器响应文档 (*.html) 文件

    \n
  2. \n
  3. 浏览器开始解析响应(*.html)并为找到的每个资源启动新请求:

    \n
      \n
    • CSS
    • \n
    • JS
    • \n
    • IMG
    • \n
    \n
  4. \n
  5. 这些请求以不可预测的顺序完成。例如,大图像的加载时间可能比 *.css 文件长,...某些资源可能已经被浏览器缓存,并且根本不会\xc2\xb4t 启动请求,...

    \n

    如果 *.js 文件的请求在IMG 请求完成之前height完成,则找不到该图像的渲染,并且新添加的 CSS 类无论如何is-visible都会启动transition...

    \n
  6. \n
  7. 一旦 IMG 请求完成(img 被渲染)。IMG 会触发内容重排。 \n需要重绘的元素(IMG)的持续过渡是“重置”,并从关键帧 0 开始。\n这可能可以解释您的问题。

    \n
  8. \n
\n
\n

以下 3 个选项可能可以解决您的问题:

\n

A. 保留图像的最终尺寸。

\n\n

B. 为图像添加一些“加载检测”并阻止转换,直到图像完全加载。

\n
    \n
  • 我们检查 img 是否已加载:\nsrc已设置并被height检测到 -
  • \n
  • 否则onload为该图像设置一个事件(因为它尚未加载)
  • \n
  • 可选:您可以对该图像使用延迟加载,并且仅“按需”加载图像(请参阅最后一个示例)。img\xc2\xb4ssrc被设置为data-src属性,src一旦图像可用,将由 JS 设置。
  • \n
\n

现在我们可以使用一个isLoaded(element)函数来排除.scroll()当前未完全加载的图像。

\n

这是jsFiddle,或扩展下面的示例......

\n
var elementsToShow = document.querySelectorAll(\'.show-on-scroll\');\n$(window).scroll(function() {\n    Array.prototype.forEach.call(elementsToShow, function (element) {\n        if (isLoaded(element) && isElementInViewport(element)) {\n            element.classList.add(\'is-visible\');\n        } else {\n            element.classList.remove(\'is-visible\');\n        }\n    });\n});\n\n[...elementsToShow].forEach((imgEl, i) => {\n    if (\n    imgEl.src &&\n    imgEl.getBoundingClientRect().height\n  ) {\n    imgEl.dataset.isLoaded = true;\n    console.log(`Img ${i} already loaded`);\n  } else {\n    console.log(`Img ${i} still loading... or should be lazyloaded`);\n\n    imgEl.onload = function(e) {\n      console.log(`Img ${i} finally loaded! onload event`);\n        e.target.dataset.isLoaded = true;\n        };\n\n    if (imgEl.dataset.src) {\n      console.log(`Img ${i} start lazy load...`);\n        imgEl.src = imgEl.dataset.src;\n    }\n  }\n})\n\nfunction isLoaded(el) {\n    return el.dataset.isLoaded\n}\n
Run Code Online (Sandbox Code Playgroud)\n

\r\n
\r\n
var elementsToShow = document.querySelectorAll(\'.show-on-scroll\');\n$(window).scroll(function() {\n    Array.prototype.forEach.call(elementsToShow, function (element) {\n        if (isLoaded(element) && isElementInViewport(element)) {\n            element.classList.add(\'is-visible\');\n        } else {\n            element.classList.remove(\'is-visible\');\n        }\n    });\n});\n\n[...elementsToShow].forEach((imgEl, i) => {\n    if (\n    imgEl.src &&\n    imgEl.getBoundingClientRect().height\n  ) {\n    imgEl.dataset.isLoaded = true;\n    console.log(`Img ${i} already loaded`);\n  } else {\n    console.log(`Img ${i} still loading... or should be lazyloaded`);\n\n    imgEl.onload = function(e) {\n      console.log(`Img ${i} finally loaded! onload event`);\n        e.target.dataset.isLoaded = true;\n        };\n\n    if (imgEl.dataset.src) {\n      console.log(`Img ${i} start lazy load...`);\n        imgEl.src = imgEl.dataset.src;\n    }\n  }\n})\n\nfunction isLoaded(el) {\n    return el.dataset.isLoaded\n}\n
Run Code Online (Sandbox Code Playgroud)\r\n
var elementsToShow = document.querySelectorAll(\'.show-on-scroll\');\n$(window).scroll(function() {\n  Array.prototype.forEach.call(elementsToShow, function(element) {\n    if (isLoaded(element) && isElementInViewport(element)) {\n      element.classList.add(\'is-visible\');\n    } else {\n      element.classList.remove(\'is-visible\');\n    }\n  });\n});\n\n[...elementsToShow].forEach((imgEl, i) => {\n  if (\n    imgEl.src &&\n    imgEl.getBoundingClientRect().height\n  ) {\n    imgEl.dataset.isLoaded = true;\n    console.log(`Img ${i} already loaded`);\n  } else {\n    console.log(`Img ${i} still loading... or should be lazyloaded`);\n    \n    imgEl.onload = function(e) {\n      console.log(`Img ${i} finally loaded! onload event`);\n      e.target.dataset.isLoaded = true;\n    };\n    \n    if (imgEl.dataset.src) {\n      console.log(`Img ${i} start lazy load...`);\n      imgEl.src = imgEl.dataset.src;\n    }\n  }\n});\n\nfunction isLoaded(el) {\n  return el.dataset.isLoaded\n}\n\n// Helper function from: http://stackoverflow.com/a/7557433/274826\nfunction isElementInViewport(el) {\n  // special bonus for those using jQuery\n  if (typeof jQuery === "function" && el instanceof jQuery) {\n    el = el[0];\n  }\n  var rect = el.getBoundingClientRect();\n  return (\n    (rect.top <= 0 &&\n      rect.bottom >= 0) ||\n    (rect.bottom >= (window.innerHeight || document.documentElement.clientHeight) &&\n      rect.top <= (window.innerHeight || document.documentElement.clientHeight)) ||\n    (rect.top >= 0 &&\n      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight))\n  );\n}
Run Code Online (Sandbox Code Playgroud)\r\n
.balltest {\n  width: 50px;\n}\n\n.fade-in {\n  opacity: 0;\n  -webkit-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;\n  -moz-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;\n  -o-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;\n  transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 0.3s 0.25s ease-out;\n  will-change: transform, opacity;\n}\n\n.standard-push {\n  opacity: 0;\n  transform: translateY(4em);\n  -webkit-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out, translateZ(0);\n  -moz-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;\n  -o-transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 1s 0.25s ease-out;\n  transition: transform 4s 0.25s cubic-bezier(0, 1, 0.3, 1), opacity 0.3s 0.25s ease-out;\n  will-change: transform, opacity;\n}\n\n.is-visible {\n  transform: translateY(0);\n  opacity: 1;\n}
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

C.等待load文档的事件

\n

您可以将 JS 初始化代码包装到load整个文档的事件中。所有资源(CSS、IMG、..)完全加载后会触发该事件。

\n
window.addEventListener(\'load\', (event) => {\n    // JS init code hier (images are loaded at this point!)\n});\n
Run Code Online (Sandbox Code Playgroud)\n