blog.Ring.idv.tw

Canvas

探討requestAnimationFrame

(圖片來源:source)

#2012-06-12  iOS 6 開始支援requestAnimationFrame

理論上「requestAnimationFrame」函式是用來處理動畫更新較好的作法,但一般來說在Canvas上要控制動畫更新的方式,大多數常用的方法不外乎透過JavaScript函式「setInterval」或「setTimeout」,基本上透過這兩個函式去控制動畫更新並不會有啥大問題,但問題是這兩個函式本質上並不是為控制動畫更新所專門設計的,我們只是藉由「setInterval」和「setTimeout」的特性將它應用在動畫更新上,所以我們需要有一個新的作法,而它就是「requestAnimationFrame」函式。所以重點就在於,為何控制動畫更新需要有「requestAnimationFrame」函式來取代「setInterval」和「setTimeout」?

理由

1. 當瀏覽器的頁面是處於最小化,或是隱藏在分頁/背景中的狀態,此時瀏覽器可能仍在執行動畫更新的動作,如此便會浪費CPU資源(demo)。

2. 大多數客戶端的螢幕更新頻率多設為60Hz,意指為平均每間隔約「16.7ms」的時間會更新一次螢幕畫面,然而重點就在於我們並不曉得客戶端的螢幕更新頻率為多少?所以若欲透過「setInterval」或「setTimeout」函式來控制,我們也無法得知理想上的動畫更新間隔時間該設為多少?而此時就可以交由「requestAnimationFrame」函式來幫我們處理,以達到同步動畫更新和螢幕更新頻率。

「requestAnimationFrame」函式被規範在「Timing control for script-based animations」標準之下,該標準最初的Working Draft是在去年六月份由Web Performance Working Group所發佈的。

參考資源

IEBLOG: USING PC HARDWARE MORE EFFICIENTLY IN HTML5, PART 1

What the requestAnimationFrame API Should Have Looked Like

requestAnimationFrame API: now with sub-millisecond precision

2012-05-14 14:58:06 | Add Comment

淺談Canvas - save/restore

CanvasHTML5規格中的一部份,它允許你可以用程式來繪製一些2D圖形或是點陣圖,通常也被拿來製作HTML5遊戲之用,而本文主要介紹Canvas API中最常被初學者混淆的一組函式,它們分別為:「save()」和「restore()」,簡單來說~ 「save()」主要是用來保存目前Canvas的狀態,例如:style, lineWidth, font等,透過「save()」函式的呼叫它會將目前Canvas的狀態「Push」到「Stack」之中,而「restore()」函式就是從「Stack」來「Pop」出上一個Canvas的狀態,讀到這邊如果您已經了解「save/restore」的話,那非常恭禧你已經掌握要領了,反之~ 如果仍然有所疑惑的話,請看下面的範例介紹:

實作範例

本文將透過兩種不同的方式來實作出上圖的範例,以方便理解為何需要「save/restore」函式,使用它有何好處?

範例1 - 不採用save/restore

範例1完整的程式碼如下:(筆者假設您已經有基本的Canvas概念,所以會跳過一些基本的解說)

<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<canvas id="a_canvas" width="200" height="150"></canvas>
<script type="text/javascript">
  var canvas = document.getElementById("a_canvas");
  var ctx = canvas.getContext("2d");
  function drawRect(context, color) {
    context.fillStyle = color;
    context.fillRect(0, 0, 100, 30);
  }
  function rotateDeg(context, deg) {
    var rad = deg * Math.PI / 180;
    context.rotate(rad);
  }
  drawRect(ctx, "red");
  ctx.translate(100,30); 
  rotateDeg(ctx, 45);    
  drawRect(ctx, "blue");
  rotateDeg(ctx, 45);    
  ctx.translate(20, -100); 
  drawRect(ctx, "green");
</script>
</body></html>

P.S. 下述圖形的虛線都作為解說之用,並非繪製在Canvas上的線條

drawRect(ctx, "red");

透過自訂的「drawRect()」函式,然後在Canvas上繪製一個從座標點(0,0)的位置畫一個長為100px寬為30px的紅色矩形。

ctx.translate(100,30); 

將目前Canvas的基準點位置設為(100,30),如上圖所示。

P.S. Canvas基準點位置預設為左上角,和Flash座標系統一致。

rotateDeg(ctx, 45);
drawRect(ctx, "blue");

透過自訂的「rotateDeg()」函式將Canvas往順時針旋轉45度,並繪製一個藍色矩形。

rotateDeg(ctx, 45);

接著再進行一次「rotateDeg()」函式將Canvas再往順時針旋轉45度。(此時已旋轉90度)

ctx.translate(20, -100);

到這邊的程式碼就是重點了,理論上如果以基準點為(0,0)的位置來說,目前要設定的相對基準點位置就是位於(200,50)的位置,但是在沒有運用「save/restore」的方式之下,我們必須自己在腦海裡進行一下矩陣轉換,同時根據上一次的基準點位置(100,30)以重設目前基準點位置(20,-100),最後再透過「drawRect(ctx, "green");」繪製出綠色的矩形出來。

範例2 - 採用save/restore

範例2完整的程式碼如下:

<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<canvas id="a_canvas" width="200" height="150"></canvas>
<script type="text/javascript">
  var canvas = document.getElementById("a_canvas");
  var ctx = canvas.getContext("2d");
  function drawRect(context, color) {
    context.fillStyle = color;
    context.fillRect(0, 0, 100, 30);
  }
  function rotateDeg(context, deg) {
    var rad = deg * Math.PI / 180;
    context.rotate(rad);
  }
  drawRect(ctx, "red");
  ctx.save();            
  ctx.translate(100,30); 
  rotateDeg(ctx, 45);    
  drawRect(ctx, "blue");
  ctx.restore();         
  ctx.translate(200,50); 
  rotateDeg(ctx, 90);    
  drawRect(ctx, "green");
</script>
</body></html>

drawRect(ctx, "red");
ctx.save();            

除了在Canvas上繪製一個紅色的矩形外,這邊還呼叫了「save()」函式,意指為將目前的基準點位置(0,0)狀態「Push」到「Stack」中,以方便待會取回。

ctx.translate(100,30); 
rotateDeg(ctx, 45);    
drawRect(ctx, "blue");

將目前Canvas的基準點位置設為(100,30),同時旋轉45度並繪製上一個藍色矩形。

ctx.restore(); 

這裡呼叫「restore()」函式的重點就是要將剛剛「Push」到「Stack」的Canvas狀態取回,也就是重設基準點位置回到(0,0),並忘掉「save()」和「restore()」之間所設定的Canvas狀態,如:translate()、rotate()。

ctx.translate(200,50); 
rotateDeg(ctx, 90);    
drawRect(ctx, "green");

最後再重設基準點位置為(200,50),並順時針旋轉90度,然後畫上綠色矩形即大功告成,到這邊可以發現端倪了嗎?假設我們不採用「save/restore」函式來輔助的話,我們需要去進行矩陣轉換,而這只是個小範例~ 如果更複雜的圖形那勢必會轉到頭昏眼花... Orz,而透過「save/restore」函式的輔助,不僅讓程式碼的可讀性大大地增加,而且維護上也較為容易些嚕。

參考資源

Understanding save() and restore() for the Canvas Context

2011-11-02 22:14:01 | Add Comment

Copyright (C) Ching-Shen Chen. All rights reserved.

::: 搜尋 :::

::: 分類 :::

::: 最新文章 :::

::: 最新回應 :::

::: 訂閱 :::

Atom feed
Atom Comment