如何在4帧之间连续制作动画

时间:2017-10-10 19:26:17

标签: javascript animation canvas

我试图制作一款平台游戏并需要在4帧之间制作动画。我有一些代码,但这会产生不稳定的动画。这是我的玩家对象:

app.config(function($routeProvider){

    $routeProvider.when("/home",{
        templateUrl:"home.html",
    })


    .otherwise('/home')

})

有更好的方法吗? player.physics()方法是我的动画代码。它未完成,因为我无法对帧进行动画处理。

1 个答案:

答案 0 :(得分:1)

哦,是的,有更好的方法可以做到这一点......

在游戏/动画中,永远不会更改HTMLImage的src

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

相反,为每个文件创建一个HTMLImage实例,在启动时加载它们并存储这些HTMLImages。存储这些的便捷方式是Array

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

不要嵌套多个这样的计时功能

setTimeoutsetInterval不是精确的计时功能,它们只告诉浏览器至少等待你传递给它的时间参数,但你不知道它有多晚,并且嵌套这个你正在添加越来越多的不精确。最后,您最后可能会在下一个setTimeout之后调用上次setInterval。再加上你现在修复的图像加载的异步性,你创建了一个漂亮的随机机器。

相反,创建一个动画循环,并检查此独特循环中的当前时间以相应地更新您的逻辑。
处理图像渲染动画(即哪一端是屏幕)时,创建此类动画循环的最佳方法是使用requestAnimationFrame 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 ];

// that code could be better arranged with Classes, please don't use as is
// I only tried to make it simple to understand how everything works independently

const urls = [
  '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(urls, startAnimation);
images.currentIndex = 0;

function startAnimation(){
  requestAnimationFrame(function(time){
    images.lastUpdate = time; // set the initial time
    animLoop(time); // start the animation
    });
  canvas.width = images[0].width;
  canvas.height = images[1].height;
  }

function imageUpdate(time){
  // only if we passed the interval
  if(time - images.lastUpdate > image_interval.value){
    images.lastUpdate = time; // save the curent time
    // update the image to be drawn
    images.currentIndex = (images.currentIndex + 1) % (images.length - 1);
    }
  }
function logicUpdate(time){
  imageUpdate(time);
  // other logic updates
  }

function draw(){
  ctx.clearRect(0,0,canvas.width, canvas.height);
  ctx.drawImage(images[images.currentIndex], 0, 0);
  }

function animLoop(time){
  logicUpdate(time); // do your logic updates based on time + external user gestures
  draw(); // separate your drawing operations
  requestAnimationFrame(animLoop); // call again at next screen refresh
}

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

const ctx = canvas.getContext('2d');
<label>set the image interval</label>
<input type="range" id="image_interval" min="0" max="2000" step="100" value="200"></label><br>
<canvas id="canvas"></canvas>