RequestAnimationFrame à un taux limité

Comme je l'ai bien compris, l'utilisation de l'API JS requestAnimationFrame est destinée aux cas où le taux d'intérêt n'a pas besoin d'être contrôlé, mais j'ai un cas d'utilisation où il est essentiel qu'un <canvas> ne soit mis à jour qu'à un certain intervalle de fps qui peut Soit entre 1 et 25 (entre 1 et 25 images par seconde , c'est-à-dire). Puis-je utiliser de façon encore efficace le RAF pour obtenir les optimisations qu'il offre?

Cette question a des similitudes avec les miennes, mais la réponse acceptée m'a donné un sens très proche à moi dans le contexte de cette question.

J'ai deux solutions possibles pour cela. Le premier consiste à utiliser une boucle while pour arrêter l'exécution du script pour un délai spécifié avant d'appeler requestAnimationFrame partir du rappel. Dans l'exemple où j'ai vu cela, il a effectivement limité les fps de l'animation, mais il semblait également ralentir l'onglet complet. Est-ce encore une bonne solution? La deuxième alternative, telle que mentionnée dans la question que j'ai associée ci-dessus, appelle requestAnimationFrame dans un setInterval . Pour moi, cela semble un peu compliqué, mais ça pourrait être la meilleure option possible.

Ou y a-t-il une meilleure alternative pour y parvenir?

La réponse de Yoshi est probablement la meilleure solution de code pour ce problème. Mais je vais toujours marquer cette réponse comme étant correcte, car après quelques recherches, j'ai constaté que ma question était invalide. requestAnimationFrame est censé garder les cadences les plus élevées possible, et il optimise les scénarios où l'animation doit être maintenue et uniforme.

Il convient de noter que vous n'avez pas besoin de requestAnimationFrame pour obtenir une optimisation (même si le RAF a été considéré comme un excellent amplificateur de performance), car les navigateurs optimisent toujours le dessin régulier d'un <canvas> . Par exemple, lorsqu'un onglet n'est pas concentré, Chrome pour un arrêter de dessiner ses toiles.

Donc, ma conclusion était que cette question était invalide . J'espère que cela aide quelqu'un qui se demande quelque chose de semblable à moi.

C'est juste une preuve de concept.

Tout ce que nous faisons est de définir nos images par seconde et les intervalles entre chaque image. Dans la fonction de dessin, nous déduisons le temps d'exécution de notre dernière image à partir de l'heure actuelle pour vérifier si le temps écoulé depuis la dernière image est supérieur à notre intervalle (basé sur la fps) ou non. Si la condition est jugée vraie, nous définissons l'heure de notre cadre actuel qui sera le «dernier temps d'exécution de la trame» dans l'appel de dessin suivant.

 var Timer = function(callback, fps){ var now = 0; var delta = 0; var then = Date.now(); var frames = 0; var oldtime = 0; fps = 1000 / (this.fps || fps || 60); return requestAnimationFrame(function loop(time){ requestAnimationFrame(loop); now = Date.now(); delta = now - then; if (delta > fps) { // Update time stuffs then = now - (delta % fps); // Calculate the frames per second. frames = 1000 / (time - oldtime) oldtime = time; // Call the callback-function and pass // our current frame into it. callback(frames); } }); }; 

Usage:

 var set; document.onclick = function(){ set = true; }; Timer(function(fps){ if(set) this.fps = 30; console.log(fps); }, 5); 

http://jsfiddle.net/ARTsinn/rPAeN/

Ce que vous pouvez faire, même si je ne sais pas vraiment si c'est vraiment mieux, c'est:

  • requestAnimationFrame dans un contexte invisible avec requestAnimationFrame
  • setInterval à jour un contexte visible avec setInterval utilisant un fps fixe

Exemple:

 <canvas id="canvas"></canvas>​ <script type="text/javascript"> (function () { var ctxVisible = document.getElementById('canvas').getContext('2d'), ctxHidden = document.createElement('canvas').getContext('2d'); // quick anim sample (function () { var x = 0, y = 75; (function animLoop() { // too lazy to use a polyfill here webkitRequestAnimationFrame(animLoop); ctxHidden.clearRect(0, 0, 300, 150); ctxHidden.fillStyle = 'black'; ctxHidden.fillRect(x - 1, y - 1, 3, 3); x += 1; if (x > 300) { x = 0; } }()); }()); // copy the hidden ctx to the visible ctx on a fixed interval (25 fps) setInterval(function () { ctxVisible.putImageData(ctxHidden.getImageData(0, 0, ctxHidden.canvas.width, ctxHidden.canvas.height), 0, 0); }, 1000/40); }()); </script> 

Demo: http://jsfiddle.net/54vWN/