【问题标题】:Detect browser graphics performance?检测浏览器图形性能?
【发布时间】:2015-02-25 21:03:28
【问题描述】:

我有一个图形密集型网站。它具有在模糊的视频背景上运行的各种 CSS3 动画。它在我的台式机和支持 GPU 的 MacBook Pro 上看起来很棒,但在我的笔记本电脑和 MacBook Air 上运行起来就像垃圾一样。

所以基本上,我想知道是否有办法使用 Javascript(或任何东西)检测浏览器图形性能,然后仅在性能达到阈值时应用 CSS 动画。该网站有一个加载屏幕,所以我有一些时间来测试性能。

我知道我可以使用 Modernizr 检测 WebGL,但不幸的是,即使性能很差,即使我的笔记本电脑的 WebGL 测试结果也呈阳性,所以我需要一个替代方案。

谢谢

【问题讨论】:

  • 看看在你的动画中使用window.requestAnimationFrame。这是一种递归方法,每次计算机屏幕刷新时只触发一次。
  • 来自 Mozilla MDN (developer.mozilla.org/en-US/docs/Web/API/window/…) "Window.requestAnimationFrame() 方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数以在下一次之前更新动画重绘”。只是不觉得“每次电脑屏幕刷新时只触发一次”是一个很好的解释。

标签: javascript css performance-testing


【解决方案1】:

正如@blazemonger 所建议的,您可以使用window.requestAnimationFrame 来检测当前的FPS 速率。据推测,如果 FPS 速率下降到某个值以下,您可以自动禁用动画。

我还建议在您网站的显眼位置放置一个“停止动画”按钮,以帮助我们这些为了可读性而更喜欢静态网站的人,以及那些不想耗尽笔记本电脑电池的人平常。

这是一个粗略的例子,如果帧速率低于 30Hz,任何动画都会停止:

var start = null;
var last  = null;
window.requestAnimationFrame(fpsMeasureLoop);
function fpsMeasureLoop(timestamp) {
    if( start == null ) {
        last = start = timestamp;
        return;
    }
    var dTime = timestamp - last;
    if( dTime > 33 ) {
        // If more than 33ms since last frame (i.e. below 30fps)
        document.getElementsByTagName("body")[0].className = "paused";
    }
    window.requestAnimationFrame(fpsMeasureLoop);
}

你需要这个 CSS:

.paused{
    -webkit-animation-play-state:paused;
    -moz-animation-play-state:paused;
    -o-animation-play-state:paused; 
    animation-play-state:paused;
}

这是有问题的,因为如果帧速率出于任何原因降至 30Hz 以下,它会阻止它们。为发现趋势帧率添加逻辑是读者的练习。

【讨论】:

  • 感谢您的帮助。不幸的是,这不起作用。即使我测试为 dTime > 0,也没有任何变化,所以我猜该功能无法正常工作。有什么想法吗?
  • @mike_freegan 我的代码中有一个关于如何计算dTime 的错误,您可能需要修复它:)
【解决方案2】:

这是一个老问题,但我会给出 2 美分,因为我最近处理过类似的事情。

我使用GreenSock 做了类似的事情来测量丢帧。但是如果你想用requestAnimationFrame实现它,我敢肯定它非常相似。

以下代码的工作原理如下:

  1. 收听动画帧

    由于我使用的是GSAP,因此我正在收听TweenLite.ticker 'tick' 事件。对于requestAnimationFrame,您可以执行相同的操作 window.requestAnimationFrame(callback);

  2. 测量每秒的帧数并更新其他数据,例如lowestFrameRate & numberOfDrops

  3. 如果条件适用,简化计算以提高 FPS

    条件可以是站点的最低 FPS (lowestFrameRate),也可以是发生丢帧的次数 (numberOfDrops)。这取决于您的具体策略

FPS(true /* showDebugBox */ );

function FPS(showDebugBox) {

  // SHOW FRAMERATE UI FOR DEBUGGING
  if (showDebugBox) {
    $('body').append([
      '<div class="hud">',
      'FPS: <span id="framerate">0</span>; ',
      'lowest FPS: <span id="lowest">null</span>; ',
      'DROPS Below 30: <span id="drops">0</span>',
      '</div>'
    ].join(''));
  }

  var $framerate = showDebugBox ? document.querySelector("#framerate") : {};
  var $lowest = showDebugBox ? document.querySelector("#lowest") : {};
  var $drops = showDebugBox ? document.querySelector("#drops") : {};
  var prevTime = 0;
  var frames = 0;
  var ticker = TweenLite.ticker;

  var fps;

  // will keep the lowest framerate the site was rendered at
  // keep in mind that the first render is almost always lower
  // than the average framerate later
  var lowestFrameRate = -1;
  // will keep tab on the number of drops below 30fps.
  // when this happens, disable some animations in order to
  // keep the requestAnimationFrame cycle short
  var numberOfDrops = 0;

  ticker.addEventListener("tick", update);

  function update() {

    var current = ticker.time;

    frames++;

    if (current > prevTime + 1) {
      fps = Math.round(frames / (current - prevTime));
      $framerate.textContent = fps;
      prevTime = current;
      frames = 0;

      // initialize lowestFrameRate with first fps value
      if (lowestFrameRate === -1) {
        lowestFrameRate = fps;

        $lowest.textContent = lowestFrameRate;
        console.info('lowest framrate initialized', lowestFrameRate);
      }

      // update lowest frame rate
      if (fps < lowestFrameRate) {
        lowestFrameRate = fps;

        $lowest.textContent = lowestFrameRate;
        console.info('lowest framerate', lowestFrameRate);
      }

      // update number of times frame rate dropped below 30
      if (fps < 30) {
        numberOfDrops++;

        // you can reduce the complexity of the animations
        // here if you want to base it on a drop below a threshold

        $drops.textContent = numberOfDrops;
        console.info('framerate dropped below 30');
      }

      // if there were more than 2 drops
      if (numberOfDrops >= 2) {
        // you can reduce the complexity of the animations here
        // if you want to base it on the number of drops.

        // you also need to define an end scenario for this thing
        // what happens if the frames continue to drop?
      }
    }
  }
}
.hud {
  position: fixed;
  top: 0;
  left: 0;
  font-family: "Lucida Console", Monaco, monospace;
  letter-spacing: 1px;
  line-height: 1.6;
  font-size: 10px;
  padding: 4px 8px;
  background: rgba(0, 0, 0, 0.3);
  pointer-events: none;
  user-select: none;
  z-index: 5000;
}

body {
  height: 500vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.0/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

【讨论】:

    猜你喜欢
    • 2013-10-06
    • 2014-08-03
    • 2012-01-20
    • 2015-09-08
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 2011-06-12
    相关资源
    最近更新 更多