代码之家  ›  专栏  ›  技术社区  ›  Mister Epic

GPU绑定的动画是否仍受CPU负载的影响?

  •  3
  • Mister Epic  · 技术社区  · 7 年前

    我已经为DIV创建了一个具有以下样式的合成层:

    div {
      position: absolute;
      height: 50px;
      width: 50px;
      background: #900;
      top: 100px;
      left: 200px;
      will-change: transform;
      transform: translateZ(0);
    }
    

    然后,我使用Web动画API对其应用动画:

    document.getElementById("box").animate(
      [
        { transform: 'rotate(0) translate3D(-50%, -50%, 0)' },
        { transform: 'rotate(360deg) translate3D(-50%, -50%, 0)' }
      ], {
        duration: 500,
        iterations: Infinity
      }
    );
    

    据我所知,这个动画现在由GPU处理,并且这个层独立于其他层,因此GPU可以独立地对它进行操作,而不必担心计算页面其他部分的几何图形。

    我不明白的是,如果我调用一个CPU密集型函数,该动画将在函数运行时完全停止,并在函数退出时再次恢复:

    function mySlowFunction(baseNumber) {
        console.time('mySlowFunction');
        var result = 0; 
        for (var i = Math.pow(baseNumber, 10); i >= 0; i--) {       
            result += Math.atan(i) * Math.tan(i);
        };
        console.timeEnd('mySlowFunction');
        return result;
    }
    
    setTimeout(() => mySlowFunction(5), 3000);
    

    有什么方法可以防止这种情况发生吗?

    https://jsfiddle.net/Lsmw85rv/4/

    1 回复  |  直到 7 年前
        1
  •  1
  •   Kaiido    7 年前

    是的,它们可能仍然会受到CPU负载的影响。
    这个 update the rendering 算法是事件循环的一部分,因此如果您以某种方式阻止了事件循环,那么您也会阻止渲染。

    现在,鼓励实施者 "spin the event loop" 当他们遇到长时间运行的代码时,这样用户界面仍然可以是被动的(而非JS驱动的动画可以继续运行),但这只是一种鼓励,所有实现都不以相同的方式进行。

    例如,在我的火狐浏览器上,我看不到你的脚本有任何的减速,甚至是下面更具攻击性的脚本,而在我的Chrome浏览器上,我可以清楚地看到渲染被阻塞。

    现在为了避免这种情况,正如在注释中所说,真正的防弹方案是在第二个线程中运行阻塞脚本。 using a Web Worker .

    document.getElementById("box").animate(
      [{
          transform: 'rotate(0) translate3D(-50%, -50%, 0)'
        },
        {
          transform: 'rotate(360deg) translate3D(-50%, -50%, 0)'
        }
      ], {
        duration: 500,
        iterations: Infinity
      }
    );
    
    function mySlowFunction(baseNumber) {
      console.time('mySlowFunction');
      const now = performance.now();
      while (performance.now() - now < baseNumber * 1000);
      console.timeEnd('mySlowFunction');
    }
    
    setTimeout(() => mySlowFunction(3), 3000);
    #box {
      position: absolute;
      height: 50px;
      width: 50px;
      background: #900;
      top: 100px;
      left: 200px;
      will-change: transform;
      transform: translateZ(0);
    }
    <div id="box"></div>