【问题标题】:canvas sprite sheet error画布精灵表错误
【发布时间】:2017-08-26 20:01:19
【问题描述】:

我在画布 spritesheet 动画上找到了一个可爱的代码 sn-p。这是ts:

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

我试图通过编写一个接受 Image 对象的动画函数来美化这段代码,这样我就可以同时为画布中的多个图像制作动画。这是我的尝试:

    $(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(){});

我收到了很多观察到的错误,但我似乎无法找到解决方法:

index3.html:59 Uncaught TypeError: 无法执行'drawImage' 'CanvasRenderingContext2D':提供的值不是类型 '(CSSImageValue 或 HTMLImageElement 或 HTMLVideoElement 或 HTMLCanvasElement 或 ImageBitmap 或 OffscreenCanvas)'

你能帮我找出我的错误吗?

【问题讨论】:

    标签: javascript html canvas sprite-sheet


    【解决方案1】:

    天命!

    quote OP “想象一下有 50 个你想要动画的精灵表。”

    50!

    看代码“接受的答案版本”

    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);
    }
    

    这是为一个精灵设置动画的最糟糕的方法,更不用说多个精灵了。

    1. 时间与显示刷新不同步。
    2. 使用requestAnimationFrame的回调创建一个定时渲染的事件,完全否定了使用requestAnimationFrame的理由。 requestAnimationFrame 告诉 DOM 您在回调中绘制的内容是动画的一部分。使用计时器进行绘制意味着您不会在请求的帧中绘制任何内容,从而使请求变得多余。
    3. requestAnimationFrame 会尽力在下一次显示刷新(1/60)之前获取所有回调,但如果在下一次刷新之前没有时间更新 DOM(交换所有脏缓冲区),它将延迟回调。您无法控制动画的时间,它们可能会在 20 毫秒到 36 毫秒或更长时间内触发,并且随着时间的推移没有一致性。
    4. 通过使用计时器在画布上绘制,您将强制为您绘制的每个精灵进行后缓冲区交换。这是一个昂贵的过程,会严重限制您绘制精灵的速度,并导致精灵在动画期间随机闪烁和剪切。

    最佳实践方式。

    • 使用由 requestAnimationFrame 触发的单个动画循环。
    • 使用数组存储所有精灵,并根据需要在主循环中更新它们。
    • 仅在主循环内渲染。不要在主循环执行之外向 DOM 渲染或做任何事情(定期)。
    • 不要在事件内部渲染,例如计时器、IO 或任何其他快速触发的事件。

    【讨论】:

      【解决方案2】:

      您还需要在使用 requestAnimationFrame 调用 animate 函数时传递 sprite 参数。

      $(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);
          };
      });
      

      【讨论】:

      • 想象有 50 个您想要制作动画的 spritesheet。那可能是 50 个函数调用或 1000 行代码,因为我没有重构代码。明确地将该代码重写为接受参数的函数是必要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-02-13
      • 2013-02-13
      • 1970-01-01
      • 2013-07-28
      • 2012-10-08
      • 2019-03-10
      • 1970-01-01
      相关资源
      最近更新 更多