J'ai trouvé un joli extrait de code sur les animations de feuilles de sprites sur toile. c'est ts:

http://jsfiddle.net/m1erickson/h85Gq/

J'ai essayé d'embellir ce code, en écrivant une fonction d'animation qui accepte les objets Image, afin que je puisse animer plusieurs images dans mon canevas simultanément. Voici ma tentative:

    $(function(){
        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
        var spritePosition=0;
        var spriteWidth=100;
        var spriteHeight=100;
        var spriteCount=40;
        var spritePlayCount=0;
        var maxSpritePlays=2;

        var objectS=new Image();
        objectS.src="sprites/first.png";

        var fps = 50;
        function animate(sprite) {
            setTimeout(function() {

                if(spritePlayCount<maxSpritePlays){
                    requestAnimationFrame(animate);
                }

                // Drawing code goes here
                ctx.clearRect(0,0,canvas.width,canvas.height);
                ctx.drawImage(sprite,spritePosition*spriteWidth, 0,spriteWidth, spriteHeight, 0,0,spriteWidth, spriteHeight);

                spritePosition++;
                if(spritePosition>spriteCount-1){
                    spritePosition=0;
                    spritePlayCount++;
                }
            }, 1000 / fps);
        }

        objectS.onload=function(){
            animate(objectS);
        }
    }); // end $(function(){});

Je reçois une erreur très observée, mais je n'arrive pas à trouver le correctif:

index3.html: 59 Uncaught TypeError: Impossible d'exécuter 'drawImage' sur 'CanvasRenderingContext2D': La valeur fournie n'est pas de type '(CSSImageValue ou HTMLImageElement ou HTMLVideoElement ou HTMLCanvasElement ou ImageBitmap ou OffscreenCanvas)'

Pouvez-vous m'aider à trouver mon bug?

0
sanjihan 1 avril 2017 à 19:44

2 réponses

Meilleure réponse

Vous devrez également transmettre le paramètre sprite lors de l'appel de la fonction animate à l'aide de requestAnimationFrame .

$(function() {
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var spritePosition = 0;
    var spriteWidth = 100;
    var spriteHeight = 100;
    var spriteCount = 40;
    var spritePlayCount = 0;
    var maxSpritePlays = 2;
    var objectS = new Image();
    objectS.src = "sprites/first.png";
    var fps = 50;

    function animate(sprite) {
        setTimeout(function() {
            if (spritePlayCount < maxSpritePlays) {
                requestAnimationFrame(function() {
                    animate(sprite);
                });
            }
            // Drawing code goes here
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(sprite, spritePosition * spriteWidth, 0, spriteWidth, spriteHeight, 0, 0, spriteWidth, spriteHeight);
            spritePosition++;
            if (spritePosition > spriteCount - 1) {
                spritePosition = 0;
                spritePlayCount++;
            }
        }, 1000 / fps);
    }
    objectS.onload = function() {
        animate(objectS);
    };
});
0
ɢʀᴜɴᴛ 1 avril 2017 à 18:56

OMDG!

quote OP "Imaginez avoir 50 spritesheets que vous voulez animer."

50!

En regardant le code "version de réponse acceptée"

function animate(sprite) {
    // create a timer event to fire in 1/50th second 
    setTimeout(function() {
         if (spritePlayCount < maxSpritePlays) {
            // create a animation frame event that may fire at some time
            // between 0 and 16ms or 32ms from now
            requestAnimationFrame(function() { 
                animate(sprite);
            });
        }
        // Drawing code etc... Make canvas dirty
        // exiting with dirty canvas outside the requestAnimationFrame
        // forces DOM to swap canvas backbuffer immediately on exit.

    }, 1000 / 50);
}

C'est la pire façon d'en animer un, et encore moins plus d'un sprite.

  1. La synchronisation n'est pas synchronisée avec l'actualisation de l'affichage.
  2. L'utilisation du rappel de requestAnimationFrame pour créer un événement chronométré qui rend, annule complètement la raison de l'utilisation de requestAnimationFrame. requestAnimationFrame indique au DOM que ce que vous dessinez dans le rappel fait partie d'une animation. L'utilisation d'une minuterie pour dessiner signifie que vous ne dessinez rien dans l'image demandée, ce qui rend la demande redondante.
  3. requestAnimationFrame fait de son mieux pour obtenir tous les rappels avant le prochain rafraîchissement de l'affichage (1 / 60e) mais cela retardera le rappel s'il n'y a pas de temps pour mettre à jour le DOM (permuter tous les tampons sales) avant le prochain rafraîchissement. Vous n'avez aucun contrôle sur la synchronisation de votre animation, ils peuvent se déclencher de 20 ms à 36 ms ou plus sans cohérence dans le temps.
  4. En utilisant des minuteries pour dessiner sur le canevas, vous forcez un échange de backbuffer pour chaque sprite que vous dessinez. Il s'agit d'un processus coûteux qui limitera considérablement la vitesse à laquelle vous pouvez dessiner des sprites, et provoquera un scintillement et un cisaillement aléatoire des sprites pendant l'animation.

La meilleure pratique.

  • Utilisez une seule boucle d'animation déclenchée par requestAnimationFrame.
  • Utilisez un tableau pour stocker tous les sprites et les mettre à jour dans la boucle principale si nécessaire.
  • Rendu uniquement à partir de la boucle principale. Ne faites pas de rendu ou ne faites rien (à intervalle régulier) dans le DOM en dehors de l'exécution de la boucle principale.
  • N'effectuez pas le rendu d'événements internes tels que les minuteurs, les E / S ou tout autre événement à déclenchement rapide.
1
Community 20 juin 2020 à 09:12