【问题标题】:Smooth Animation in HTML5 Canvas [duplicate]HTML5 Canvas 中的平滑动画 [重复]
【发布时间】:2016-06-25 18:41:53
【问题描述】:

所以我用 HTML 和 Javascript 在画布上创建了一个游戏。我想制作一些飞扬的小鸟游戏,但是当我按下一个键时,玩家的动画看起来非常卡顿。看看:

body {
    overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="Style.css"/>
</head>
<body onload="startgame()">
    <canvas id="canvas"></canvas>
<script>
    canvas.height=window.innerHeight;
    canvas.width=window.innerWidth;

function startgame() {
    var c = document.getElementById("canvas");
    var ctx = c.getContext("2d");
    
    var x = 900;
    var y = 300;
    var w = 25;
    var h = 500;
    var yperson = 20;
    var xperson = 200;
    
    document.addEventListener("keydown", function() {
        yperson -= 150;
    });
    
    function updateperson() {
        yperson = yperson;
    }
    
    setInterval(createobject, 10);
    function createobject() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        x -= 1;
        yperson += 0.5;
        yperson *= 1.003;
        
        ctx.fillStyle = "#009999";
        ctx.fillRect(x,y,w,h);
        
        ctx.fillStyle = "black";
        ctx.fillRect(xperson,yperson,30,30);
        
        if (x <= 50) {
            if (yperson < 280 && xperson === x-30) {
                x -= 1;
            } else if (yperson > 280){
                x += 1;
            }
        }
      
        
    }
}
</script>
</body>
</html>

我希望它有一个流畅的动画。我看到有人说应该用 requestanimationframe 来完成,但我不知道如何使用它。

提前致谢。

【问题讨论】:

  • 对我来说看起来不错,但是是的,requestAnimationFrame 会因为它更新显示的方式而略微平滑。
  • 我知道。 @SpencerWieczorek 但是当你按下一个键时,这个人会在没有动画的情况下快速上升。
  • 您只是直接将位置向上移动 150 像素。如果你想要动画,你想向上施加一个力,而不是直接向上平移;在这种情况下,两者之间没有动画。
  • 好的,但是我如何给一些动画做广告呢? @SpencerWieczorek
  • @MartijnVenvande 您的问题重复实际上是一件好事:a)您无需等待即可找到所有相关的、已评分的答案,b)现在更多不同措辞的问题描述了相同的问题,增加第三方快速找到答案的可能性。因此,请考虑恢复您上次的编辑。

标签: javascript html animation canvas requestanimationframe


【解决方案1】:

requestAnimationFrame

详情请见MDN window.requestAnimationFrame

由于上一个答案缺少一些信息,这里是基本用法的注释示例。

// A flag to indicate that the animation is over
var stop = false; // when true the animation will stop

// define main loop update
// the callback that is the main loop
// the browser treats this function as special in terms of display items including
// the canvas, and all DOM items.
// It will ensure that any changes you make to the page are synced to the display
function update(time){  // time is the time since load in millisecond 1/1000th
                        // time is high precision and gives the time down to
                        // microseconds (1/1,000,000) as fraction 0.001 is one microsecond

    // you can stop the animation by simply not calling the request
    // so if the flag stop is true stop the animation
    if(!stop){
        requestAnimationFrame(update); // request the next frame   
    }
}

requestAnimationFrame(update); // request the very first frame 
// or you can start it with a direct call. But you will have to add the time
update(0);

更新函数每秒最多调用 60 次。如果代码跟不上(即渲染时间超过 1/60 秒),则更新函数将等待下一帧,从而有效地将帧速率降低到 1/30。如果渲染很慢,它会继续跳帧。

由于您无法控制帧速率,您可以执行以下操作将动画减慢到所需的帧速率。

const FRAMES_PER_SECOND = 30;  // Valid values are 60,30,20,15,10
// set the mim time to render the next frame
const FRAME_MIN_TIME = (1000/60) * (60 / FRAMES_PER_SECOND) - (1000/60) * 0.5;
var lastFrameTime = 0;  // the last frame time
function update(time){
    if(time-lastFrameTime < FRAME_MIN_TIME){ //skip the frame if the call is to early
        requestAnimationFrame(update);
        return; // return as there is nothing to do
    }
    lastFrameTime = time; // remember the time of the rendered frame
    // render the frame
    requestAnimationFrame(update);
}

如果您将焦点更改为另一个选项卡,则浏览器将不再调用请求,直到焦点返回到该选项卡。

和其他定时器事件一样,调用requestAnimationFrame返回一个ID,可以用来取消回调事件

var id = requestAnimationFrame(update);
// to cancel 
cancelAnimationFrame(id);

您实际上可以每帧多次调用 requestAnimationFrame。只要所有请求都可以在 1/60 秒内呈现,它们都将被同步并同时呈现到显示器上。但是你必须小心,因为如果渲染时间太长,它们可能会不同步。

RequestAnimationFrame 通过双缓冲更改来防止闪烁(渲染未完成时显示画布)。与显示硬件同步并防止剪切(当显示在框架中途更新并且显示的上半部分显示旧框架而底部显示新框架时引起)。根据浏览器的不同,还有更多好处。

【讨论】:

    【解决方案2】:

    这就是我设置游戏的方式:

     // DEFINE OBJECTS UP HERE
    
     var update = function(modifier) {
         // update all the object properties
         // multiply values that depend on time (like speeds) by modifier
     };
     var render = function() {
         // draw everything
     };
     var main = function() {
         var now = Date.now();
         var change = now - then;
    
         update(change/1000); // update based on frame rate, change in milliseconds/second
         render();
         then = now;
         requestAnimationFrame(main);
     };
    
     // ADD EVENT LISTENERS HERE
    
     requestAnimationFrame = window.requestAnimationFrame
                     || window.webkitRequestAnimationFrame
                     || window.msRequestAnimationFrame 
                     || window.mozRequestAnimationFrame;
     // ABOVE CODE GIVES CROSS-BROWSER COMPATIBILITY
    
      var then = Date.now();
      main();
    

    requestAnimationFrame 告诉浏览器根据帧速率执行循环。就个人而言,我不明白它是如何工作的,尽管如果这里有人这样做,我会非常有兴趣了解更多。 setInterval 允许您设置循环运行的速度,但最佳速度取决于浏览器。 “then”和“now”变量用于确定自上次执行循环以来已经过去了多长时间。该值可以传递给更新函数并用于依赖于帧速率的计算,尽管有时您不需要它并且可以使用:

     var update = function() {
          //STUFF
     };
    
     // if using that variation just ignore then and now and call:
     update();
     //in your main
    

    虽然使用 then 和 now 更好。

    【讨论】:

      猜你喜欢
      • 2013-08-09
      • 2013-05-13
      • 1970-01-01
      • 2012-10-26
      • 2011-12-25
      • 2015-06-18
      • 1970-01-01
      • 2014-05-28
      • 2019-12-07
      相关资源
      最近更新 更多