2014-05-23 67 views
0

我想創建一個小的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); 
      } 
     } 
    } 
} 

所以,我怎麼能調用randomFill()函數來重畫在畫布上說,一個100毫秒後。 我想在畫布上顯示數組中的變化,但經過一點延遲後,讓人們可以看到。

+0

爲什麼不直接使用setTimeout? – swornabsent

回答

1

要耽誤你需要使用別的東西比一個for循環的延遲將涉及它們無一例外異步定時器迭代,並且不能爲此使用在for循環迭代中「休眠」。

解決方案是將for-loops分解成每100ms遞增的計數器。它變得有點不同,但可以讓你爲每個「迭代」(或增量)引入一個延遲。

這是一個基本的例子設置使用計數器和延遲每次迭代循環 - 的例子僅作爲骨架,您可以使用採用您的方案:

/* 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代碼將與全球窗口對象範圍執行的,所以宣佈ij全球使他們可以在函數內部。有辦法解決這個問題,但爲了簡單起見,我將這個主題留在這裏。

+0

非常感謝。我應用了它,它工作。我看着錯誤的方向。謝謝。 –

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(...) 
+0

我嘗試setTimeout setTimeout(randomFill(),1000);在我想更新畫布的地方,但沒有任何效果。只顯示畫布中的最終圖像,而不顯示過渡。 –

+0

太近了! setTimeout接受一個函數。當你調用'setTimeout(randomFill(),x)'時,你沒有傳遞一個函數塊,你正在執行randomFill()並將返回值傳遞給setTimeout()。你要麼按照我在答案中列出的方式,要麼'setTimeout(randFill,x)'(注意:沒有括號) – swornabsent

+0

仍然沒有線索。所以我把setTimeout(function(){randomFill();},1000);無論我需要更新畫布。但畫布仍然等待1秒鐘並顯示最終圖像。 –

1

創建動畫的新首選方式循環是window.requestAnimationFrame

這是首選,因爲它將循環執行與瀏覽器刷新率協調以生成高效重繪。如果瀏覽器切換到不同的瀏覽器選項卡,它也會暫停動畫循環(將電池節省在移動設備上)。

像setTimeout,requestAnimationFrame(我將稱之爲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> 
+0

我可以以這樣一種方式使用它,只要我的算法循環,它就會使用randomFill()更新ARR元素的值來更新畫布。讓它保持在那裏100毫秒,而不是執行下一次迭代。我會怎麼做? –

+0

當然,要做到這一點,你需要另一個循環,並在你做了randomFill之後重置'lastTime'變量。這樣,在計時器再次運行之前,您將獲得完整的100毫秒。看到我編輯的答案。 – markE

+0

哦,我是一個總noob。我仍然無法讓它正常工作。好的,我複製粘貼你的代碼。但沒有任何反應。我從互聯網上的很多資源中讀取,但仍然無法工作。我唯一想要的是,如果你看看我的算法循環,那就是每當我更新arr元素的值時,我只是想用randomFill()重畫畫布,但也需要暫停一下,以便可以看到過渡。總之,我只是想放慢循環中的每一次迭代。 –