web-dev-qa-db-ja.com

requestAnimationFrameと固定フレームレートを使用する最良の方法は何ですか

特にJank Bustersの話を見た後、私は最近、アニメーションが多いWebサイトでHTML5 -requestAnimationFrame- APIをたくさん使用するようになりました。これはかなりうまくいくようで、多くの場合実際にパフォーマンスを向上させます。

まだ1つの質問がまだ私に残っています:[〜#〜] [〜#〜]完全に計算されていないアニメーションを使用したい場合(たとえば、スプライトシートを考えてください)固定フレームレートを目指します。もちろん、setIntervalを再び使用することもできますが、これに対処する他の方法があるかもしれません。

固定フレームレートでrequestAnimationFrameを使用する方法は2つ考えられます。

var fps = 25; //frames per second

function animate(){

   //actual drawing goes here
   setTimeout(function(){
      requestAnimationFrame(animate);
   }, 1000 / fps)

}

animate();

または

var fps = 25; //frames per second

var lastExecution = new Date().getTime();

function animate(){

    var now = new Date().getTime();

    if ((now - lastExecution) > (1000 / fps)){
        //do actual drawing
        lastExecution = new Date().getTime();
    }

    requestAnimationFrame(animate);

}

animate();

個人的には、2番目のオプション(最初のオプションは不正行為のように感じる)を選択しますが、特定の状況では、それはbuggyのようです。

このアプローチは本当に価値がありますか(特に12.5のような低いフレームレートで)?改善すべき点はありますか?これに取り組む別の方法はありますか?

4
m90

アニメーションは、計算が完全に時間の関数であっても計算する必要があります。また、いつでも[アニメーション]画像をdraw()でき、適切なフレームを取得していることを確認できます。あなたはこのようなものを望みますが、必ずしもそうではありませんthis

// whatever your "attributes" looks like, the point is you've given enough
// information to the constructor to find ordered frames in the image and know
// the framerate
function SpriteAnimation(image, attributes) {

  // you probably want a SpriteFrame "class" to populate here, based on whatever
  // existing code you use to extract frames from the Sprite
  this.frames = [ SpriteFrame, SpriteFrame, ... ];
                         // ... getFramesFromImageAndAttributes(image, attributes)

  // if the animation is running, this is the time it started
  this.startTime = 0;

  // whatever your framerate is ... 
  this.fps = 25;         // getFPSFromAttributes(frame_attributes)

  // whether the animation loops infinitely
  this.loop = true;      // getLoopFlagFromAttributes(frame_attributes)

  // for canceling the animationframe request, if necessary
  this.animationFrameRequestId = null;

  // draw whichever frame should be visible *right now*
  this.draw = function() {

    // unconditionally show the first frame if the animation isn't running
    if (this.startTime == 0) {
      this.frames[0].draw();
      return;
    }

    // there may be a better and more accurate way to compute this ...
    var frame_duration = 1000 / this.fps;
    var now = (new Date()).getTime();
    var elapsed_time = now - this.startTime;
    var visible_frame = Math.floor(elapsed_time / frame_duration);

    if (visible_frame_number > frames.length) {
      if (!this.loop) {
        // we're past the end of the animation and we're not looping.
        // stop the animation.
        this.startTime = 0;
        visible_frame = 0;
      }
    }

    this.frames[visible_frame % frames.length].draw();

    if (this.startTime != 0) {
      var _t = this;
      requestAnimationFrame(_t.draw);
    }

  }

  this.animate = function() {
    this.startTime = (new Date()).getTime();
    var _t = this;
    requestAnimationFrame(_t.draw);
  }

  this.stop = function() {
    this.startTime = 0;
    if (this.animationFrameRequestId) {
      cancelAnimationFrame(this.animationFrameRequestId);
    }
  }

}

var a = new SpriteAnimation("/path/to/image.jpeg", {
  fps: 25,
  frame_width: 100,
  frame_height: 100,
  image_width: 1000,
  image_height: 10000
});
a.animate();

...または何でも。

2
svidgen

シームレスなアニメーションの場合、スプライトベースでも2番目のオプションであるDate.now()を使用します。忘れないで

戻り値

requestIDは、コールバックリストのエントリを一意に識別する長整数値です。これはゼロ以外の値ですが、その値について他の仮定を行うことはできません。この値をwindow.cancelAnimationFrame()に渡して、更新コールバック要求をキャンセルできます。

(from [〜#〜] mdn [〜#〜]

0
Greg