zoukankan      html  css  js  c++  java
  • JS:指定FPS帧频,requestAnimationFrame播放动画

    Flash制作动画,最基础的概念就是帧,但在Flash中,帧频的控制比较简单,只需要编译前指定一下目标帧频就可以了。

    实际运行时,不需要我们关心定时器的问题,flash player会定时触发EnterFrame消息,推动Movieclip播放。

    在js这一侧,需要我们设定一个定时器,并推动相应的绘制逻辑执行。

    最简单:

    var FPS = 60;
    
    setInterval(draw, 1000/FPS);

    这个简单做法,如果draw带有大量逻辑计算,导致计算时间超过帧等待时间时,将会出现丢帧。除外,如果FPS太高,超过了当时浏览器的重绘频率,将会造成计算浪费,例如浏览器实际才重绘2帧,但却计算了3帧,那么有1帧的计算就浪费了。

    成熟做法:

    引入requestAnimationFrame,这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数,以满足开发者操作动画的需求。

    这个函数类似setTimeout,只调用一次。

    function draw() { 
            requestAnimationFrame(draw); 
            // ... Code for Drawing the Frame ... 
    }

    递归调用,就可以实现定时器。

    但是,这样完全跟浏览器帧频同步了,无法自定义动画的帧频,是无法满足需求的。

    接下来需要考虑如何控制帧频。

    简单做法:

    var fps = 30;
    function tick() {
      setTimeout(function() {
        requestAnimationFrame(tick);
        draw(); // ... Code for Drawing the Frame ...
      }, 1000 / fps);
    }
    tick();

    这种做法,比较直观的可以发现,每一次setTimeout执行的时候,都还要再等到下一个requestAnimationFrame事件到达,累积下去会造成动画变慢。

    自行控制时间跨度:

    var fps = 30;
    var now;
    var then = Date.now();
    var interval = 1000/fps;
    var delta;
    
    function tick() {
      requestAnimationFrame(tick);
      now = Date.now();
      delta = now - then;
      if (delta > interval) {
        // 这里不能简单then=now,否则还会出现上边简单做法的细微时间差问题。例如fps=10,每帧100ms,而现在每16ms(60fps)执行一次draw。16*7=112>100,需要7次才实际绘制一次。这个情况下,实际10帧需要112*10=1120ms>1000ms才绘制完成。
        then = now - (delta % interval);
        draw(); // ... Code for Drawing the Frame ...
      }
    }
    tick();

    针对低版本浏览器再优化:

    如果浏览器没有requestAnimationFrame函数,实际底层还只能用setTimeout模拟,上边做的都是无用功。那么可以再改进一下。

    var fps = 30;
    var now;
    var then = Date.now();
    var interval = 1000/fps;
    var delta;
    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
    
    function tick() {
      if(window.requestAnimationFrame)
       {
          requestAnimationFrame(tick);
          now = Date.now();
          delta = now - then;
          if (delta > interval) {
            // 这里不能简单then=now,否则还会出现上边简单做法的细微时间差问题。例如fps=10,每帧100ms,而现在每16ms(60fps)执行一次draw。16*7=112>100,需要7次才实际绘制一次。这个情况下,实际10帧需要112*10=1120ms>1000ms才绘制完成。
            then = now - (delta % interval);
            draw(); // ... Code for Drawing the Frame ...
          }
       }
       else
       {
           setTimeout(tick, interval);
        draw(); } } tick();


    最后,还可以加上暂停。

    var fps = 30;
    var pause = false;
    var now;
    var then = Date.now();
    var interval = 1000/fps;
    var delta;
    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
    
    function tick() {
         if(pause)
              return;
       if(window.requestAnimationFrame)
         {
        ...
         }
         else
         {
              ...
         }
    }
    tick();
  • 相关阅读:
    事后诸葛亮
    OVS常用命令
    阿里云部署杂记
    Alpha冲刺总结
    测试随笔
    Alpha冲刺集合
    项目Alpha冲刺Day12
    项目Alpha冲刺Day11
    项目Alpha冲刺Day10
    MySQL修改密码
  • 原文地址:https://www.cnblogs.com/kenkofox/p/3849067.html
Copyright © 2011-2022 走看看