如何在画布中添加动画延迟?

时间:2014-05-23 14:28:36

标签: javascript canvas

我正在尝试创建一个小的javascript动画来更好地理解算法。 该算法适用于像这样的二维数组: -

function algorithm(){
    for(var i=0;i<gridSize;i++){
        for(var j=0;j<gridSize;j++){
            if(arr[i][j]=="D"){
                // fill every nearby emty place with 1
            if(i-1>=0 && j-1>=0 && arr[i-1][j-1]=="-"){
                arr[i-1][j-1]=1;
                // change the canvas here.
                    queue.enqueue(new Point(i-1,j-1));
                }
            }
        }
    }
}

等等。 我基于数组填充画布的功能如下:

function execute(){
    for(var i=0;i<gridSize;i++){
        for(var j=0;j<gridSize;j++){
            drawRect(i,j);
        }
    }
}
function randomFill(){
    ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height)
    execute();
    for(var i=0;i<gridSize;i++){
        for(var j=0;j<gridSize;j++){
            if(arr[i][j]=="W")
                ctx.fillStyle = "red";
            else if(arr[i][j]=="D")
            ctx.fillStyle = "blue";
            else if(arr[i][j] == "-")
            ctx.fillStyle = "green";
            else
            ctx.fillStyle = "purple";
            ctx.font='30px Calibri';
            ctx.fillText(arr[i][j],i*40 + 40,(j+1)*40 + 40);
            }
        }
    }
}

那么如何在100毫秒之后调用randomFill()函数在画布上重绘。 我想在画布上显示数组中的变化,但是稍微延迟之后,人们就可以看到了。

3 个答案:

答案 0 :(得分:1)

创建动画循环的新方法和首选方法是使用window.requestAnimationFrame

这是首选,因为它使用浏览器刷新率协调循环执行,以生成有效的重绘。如果浏览器切换到不同的浏览器选项卡(在移动设备上节省电池电量),它还会暂停动画循环。

与setTimeout类似,requestAnimationFrame(我将其简称为RAF)被赋予一个回调函数来执行。 RAF将在与浏览器和硬件同步的庄园中执行该回调函数。

以下是典型的RAF动画循环:

function animate(timestamp){

    // execute RAF again to request the next loop
    requestAnimationFrame(animate);

}

RAF向其调用的函数发送时间戳(这是动画中的时间戳)。您可以使用此时间戳在100ms后执行randomFill()。

可能如下所示:http://jsfiddle.net/m1erickson/2afLc/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" />
<script src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
    $(function(){

        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
        ctx.fillStyle="skyblue";
        ctx.strokeStyle="lightgray";
        ctx.lineWidth=4;

        // testing: rotate a rectangle every 100ms        
        var r=0;
        var interval=100;
        $("#interval").text("Call randomFill to rotate every "+interval+"ms");

        // a variable to hold when the animation loop last fired
        var lastTime;

        // start the animation loop
        requestAnimationFrame(animate);

        function animate(timestamp) {

            // initialize lastTime during the first run of the animate() loop
            if(!lastTime){lastTime=timestamp;}

            // calculate the elapsed time
            var elapsed=timestamp-lastTime;      


            if(elapsed>interval){
                // let randomFill complete before
                // resetting the timer and requesting another loop
                r+=Math.PI/120;
                randomFill(r);
                // reset the timer
                lastTime=performance.now();
            }
            // request another animation loop
            requestAnimationFrame(animate);

        }        

        function randomFill(r){
            ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height)
            ctx.save();
            ctx.translate(100,100);
            ctx.rotate(r);
            ctx.fillRect(-25,-25,50,50);
            ctx.strokeRect(-25,-25,50,50);
            ctx.restore();
        }

    }); // end $(function(){});
</script>
</head>
<body>
    <p id="interval">Call randomFill</p>
    <canvas id="canvas" width=350 height=350></canvas>
</body>
</html>

答案 1 :(得分:1)

要延迟迭代,你需要使用除了for-loop以外的其他东西,因为延迟会涉及异步的异常,而且不能用于&#34; sleep&#34;在for循环迭代中。

解决方案是将for循环分解为计数器,每100ms递增一次。它变得有点不同,但是你可以为每个&#34;迭代引入一个延迟&#34; (或增加)。

这是一个基本的示例设置循环使用计数器和每次迭代的延迟 - 该示例仅作为您可以用于您的场景的骨架:

/* global variables */
var i = 0;                 // outer loop
var j = 0;                 // inner loop

function next() {

    if (j === gridSize) {
        i++;               // increase outer loop counter
        j = 0;             // reset inner loop counter

        if (i === gridSize) {
            /* ...loop has finished, re-initialize etc. here ... */
            return;
        }
    }

    /* ... use i, j here and update canvas - see example link below ... */

    j++;                   // iterate inner counter

    setTimeout(next, 100); // delayed calling of next() again
}
next();                    // start loop

<强> EXAMPLE FIDDLE

next()方法每次调用时都会增加内循环(j)。当满足第一个条件时(j = gridSize),重置j并增加外循环(i)。

当外循环满足条件(i = gridSize)时,循环进入退出点,可用于重新初始化数组,重置ij,重启循环等等。

ij位于全局范围内,因为当您调用setTimeout时,代码将以全局窗口对象作为范围执行,因此声明i和{ {1}}全局使它们在函数内部可用。有很多方法,但为了简单起见,我将这个主题留在这里。

答案 2 :(得分:0)

setTimeout

function randomFill(){
  setTimeout(function() {
    ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height)
    execute();
    for(var i=0;i<gridSize;i++){
        for(var j=0;j<gridSize;j++){
            if(arr[i][j]=="W")
                ctx.fillStyle = "red";
            else if(arr[i][j]=="D")
            ctx.fillStyle = "blue";
            else if(arr[i][j] == "-")
            ctx.fillStyle = "green";
            else
            ctx.fillStyle = "purple";
            ctx.font='30px Calibri';
            ctx.fillText(arr[i][j],i*40 + 40,(j+1)*40 + 40);
            }
        }
    }
  }, 100); // or however long the delay should be, in ms
}

建议您确保不要使用clearTimeout重叠超时请求,例如:

  tid && clearTimeout(tid);
  tid = setTimeout(...)