当我尝试在 HTML5 Canvas 中移动 PNG 图像时,图像不稳定

PNG image choppy when I try to move it in HTML5 Canvas

所以我来的时候正在用HTML5Canvas和纯Javascript做游戏 遇到一个恼人的问题。当我尝试使用 setInterval 移动我的图片时,它可以工作,但图片会像波涛汹涌一样抽动。我的猜测是,这与图像需要每 10 毫秒加载一次有关。请帮我解决这个问题。

示例代码如下:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta charset = "UTF-8" />
    </head>
    <body>
        <canvas id = "myCanvas" width = "1000" height = "500" style="border:1px solid #000000;">
        </canvas>
        <script type = "text/javascript">
            var canvas = document.getElementById("myCanvas");
            var ctx = canvas.getContext("2d");
            var x = 0;
            var image = new Image();
            image.src = 'spaceship.png';

            function draw(){
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                image.src = 'spaceship.png';
                image.onload = function(){
                    ctx.drawImage(image, x, 0);
                }
                x += 2;
            }
            setInterval(draw,10);
        </script>
    </body>
</html> 

首先,10ms的间隔太短了。即使是 60fps,你也会有 ~16.67ms 的间隔。

此外,如果可用,您在制作动画时不应使用 setInterval,而应使用 requestAnimationFrame。这应该会导致支持它的浏览器上的动画更加流畅。

最后应该优化的是你每帧将图像移动 2px。您不能确定每个帧都在相同的时间间隔后被调用。像这样,您将在快速计算机上获得更快的动画,而在慢速计算机上获得慢速动画。您应该使用当前时间和自上次动画帧以来经过的时间增量(例如 Date.now()

最后要注意的是图像的 onload 回调只能调用一次。那是在执行任何动画或绘图之前。所以只有在加载图像后,动画才会开始。

移动球的例子:

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    lastTimestamp,
    imageX = 0;

canvas.width = 300;
canvas.height = 300;

var image = new Image();
image.onload = function () {
  requestAnimationFrame(draw);
};
image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAolBMVEUAAAARqu4YrO4bre4aq+wbrOocrOocq+sbreobquocrOkcrOobq+oaqugdresir+sosewus+w0te01tu06t+0+ue4/ue5GvO5Ov+9cxPBfxfFoyPFwy/J0zPJ4zvN6z/OA0fSE0vSG0/SJ1PSQ1vWR1/WT1/WV2PWY2faZ2vad2/ai3fej3fel3ven3/ev4vix4/i45vm65vm85/m+6PnA6fpEpw5TAAAADnRSTlMADx8vX29/j5+vv8/f79ErPTAAAADdSURBVBgZBcELThsxFADAef5sQkAVEr3/JcsSNrafOxNAKT2wRyYIiFsPwB7XRqC+FY9Smpnjks9FEO/l7c8BOP+NPLfKe/34qgDHx5VtqI7j9hcQrdV9/917lbj5BGhHa618ukVp8TgAGpr7I1rpDgAbyaGXagMYyxpstYUyOmAtuIpouFavAOYr0XZkmbO2FthzJGm31ebBWhcA0yrDAgBYRlmuBADmr1XytU8AkKdXVqvvbAFgnplPldVz9gDM79zPVNmjuUTBvH6sMwmIewdwvTYCqL0H9msk+A8oZ3I4nbdKXwAAAABJRU5ErkJggg==";

function draw() {
  var now = Date.now(),
      timeDelta = (now - (lastTimestamp || now)) / 1000; // in seconds
  imageX += timeDelta * 30; // meaning: 30px per second
  
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(image, imageX, 0);
  
  lastTimestamp = now;
  requestAnimationFrame(draw);
}
<canvas id="canvas" />