Javascript 如何在4帧之间连续设置动画

Javascript 如何在4帧之间连续设置动画,javascript,animation,canvas,Javascript,Animation,Canvas,我正在尝试制作一个平台游戏,需要在4帧之间设置动画。我有一些代码,但这会产生不稳定的动画。这是我的播放器对象: var player = {}; player.x = 0; player.y = 0; player.vx = 0; player.vy = 0; player.facing = ""; player.jumping = false; player.shooting = false; player.animating = true; playe

我正在尝试制作一个平台游戏,需要在4帧之间设置动画。我有一些代码,但这会产生不稳定的动画。这是我的播放器对象:

  var player = {};
  player.x = 0;
  player.y = 0;
  player.vx = 0;
  player.vy = 0;
  player.facing = "";
  player.jumping = false;
  player.shooting = false;
  player.animating = true;
  player.animationSpeed = 100;
  player.frame = 0;
  player.img = new Image();
  player.src = null;
  //I edited out the frames for length issues. They are player.frame0 through player.frame31
  player.physics = function() {
    document.onkeydown = function(e) {
      e = e || window.event;
      if (e.keyCode == 37) {
        player.vx -= player.vx;
        player.vx -= 3;
        player.facing = "left";
        player.animating = true;
      } else if (e.keyCode == 38) {
        player.vy -= player.vy;
        player.vy -= 10;
        player.jumping = true;
        player.animating = true;
      } else if (e.keyCode == 39) {
        player.vx -= player.vx;
        player.vx += 3;
        player.facing = "right";
        player.animating = true;
      } else if (e.keyCode == 32) {
        player.shooting = true;
        player.animating = true;
      }
    }
    document.onkeyup = function() {
      player.animating = false;
      player.jumping = false;
      player.vx = 0;
      player.vy = 0;
    }
    if (player.facing.toLowerCase() === "right" && player.animating === true) {
      setInterval(function() {
        player.src = player.frame0;
        setTimeout(function() {
          player.src = player.frame1;
          setTimeout(function() {
            player.src = player.frame2;
            setTimeout(function() {
              player.src = player.frame3;
            },player.animationSpeed);
          },player.animationSpeed);
        },player.animationSpeed);
      },player.animationSpeed);
    }
    drawImg(player,player.x,player.y);
  }

有更好的方法吗?player.physics()方法是我的动画代码所在的位置。这是未完成的,因为我无法设置帧的动画。

哦,是的,有更好的方法来完成

在游戏/动画过程中,永远不要更改HTMLImage的
src
。 设置HTMLImage的
src
始终是异步的*。这意味着,当您要求绘制它时,它可能仍然是加载的前一个图像,并且您最终实际绘制了错误的图像。
*事实上,在某些浏览器中,此操作是在并行线程中执行的,可能在下一行代码执行时完成,但您不能依赖于此,太多的参数可能会产生干扰

相反,每个文件创建一个HTMLImage实例,在启动时加载它们并存储这些HTMLImage。存储这些数据的方便方法是将其存储在
数组中

// load images from an Array of URLs, returns an Array of HTMLImages
// accepts an callback which will fire when all images will have loaded
function loadImages(arrayOfURLs, onloadAll){
  let loaded = 0;
  function onloadSingle(e){
    if(++loaded >= arrayOfURLs.length){
      onloadAll();
    }
  }
  return arrayOfURLs.map(function(url) {
    let img = new Image();
    img.onload = onloadSingle;
    img.src = url;
    return img;
  });
}
不要像那样嵌套多个计时函数
setTimeout
setInterval
不是精确的计时功能,它们只告诉浏览器至少等待您传递给它的时间参数,但您不知道它有多晚,通过嵌套它,您正在增加越来越多的不精确性。最后,您可能会在下一个
setInterval
之后调用上一个
setTimeout
。再加上现在固定的图像加载异步性,您就创建了一个很好的随机机器

相反,创建一个动画循环,并检查此唯一循环中的当前时间以相应地更新逻辑。
在处理图像渲染动画(即屏幕的哪一端)时,创建此类动画循环的最佳方法是使用(rAF)方法。
此方法接受一个回调函数,该函数将在下次屏幕刷新之前调用(在大多数计算机上约为@60fps)。这允许您仅在真正需要时以恒定的帧速率进行更新。
传递给它的回调将有一个参数,一个HighResTimeStamp,它允许您将逻辑建立在精确的时间上

非常基本的动画循环可能如下所示:

function animLoop(time){
  logicUpdate(time); // do your logics update based on time + external user gestures
  draw(); // separate your drawing operations
  requestAnimationFrame(animLoop); // call again at next screen refresh
}
然后,您需要做的就是存储图像数组的currentIndex,并以每个间隔递增它,将数组的长度模化为-1

// at every interval that you may decide based on rAF time
currentIndex = (currentIndex + 1) % (images.length - 1);
currentImage = images[ currentIndex ];
//代码可以更好地与类一起排列,请不要按原样使用
//我只是想简单地理解每件事是如何独立运作的
常量URL=[
“x8hwadksz3bz7za/banana-1.png”,
“hl7d2kzr4yvoi37/banana-3.png”,
“tqrlzaujru03e9w/banana-5.png”,
“d1gbyfyiz90w152/banana-7.png”
].map(u=>'https://dl.dropboxusercontent.com/s/"u",;
const images=loadImages(URL、startAnimation);
images.currentIndex=0;
函数startAnimation(){
requestAnimationFrame(函数(时间){
images.lastUpdate=time;//设置初始时间
animLoop(时间);//启动动画
});
canvas.width=图像[0]。宽度;
canvas.height=图像[1]。高度;
}
函数imageUpdate(时间){
//除非我们通过了休息时间
if(time-images.lastUpdate>image\u interval.value){
images.lastUpdate=time;//保存当前时间
//更新要绘制的图像
images.currentIndex=(images.currentIndex+1)%(images.length-1);
}
}
功能逻辑更新(时间){
图像更新(时间);
//其他逻辑更新
}
函数绘图(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(图像[images.currentIndex],0,0);
}
函数循环(时间){
logicUpdate(时间);//根据时间+外部用户手势进行逻辑更新
draw();//分离绘图操作
requestAnimationFrame(animLoop);//下次屏幕刷新时再次调用
}
//从URL数组加载图像,返回HTMLImages数组
//接受一个回调,该回调将在加载所有图像时触发
函数加载映像(ArrayFurls、onloadAll){
设加载=0;
函数onloadSingle(e){
如果(++加载>=阵列卷绕长度){
onloadAll();
}
}
返回arrayOfURLs.map(函数(url){
设img=新图像();
img.onload=onloadSingle;
img.src=url;
返回img;
});
}
const ctx=canvas.getContext('2d')
设置图像间隔


为什么要使用iframes?iframes是什么?我当时可能不理解你的问题。你能发布你的应用程序的HTML代码吗?我没有。这是纯Javascript。因此,请提供更多关于
player
变量的信息,因为我们不知道它是什么。我将您的代码压缩为一个名为playerLogic的函数。它工作正常,但如果我试图在代码中更改图像数组,它将停止绘制精灵。以下是函数和播放器对象: