高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

EaselJS新手上路

前言

我一直都沒有忘記我是個閃客(Flasher)的身份,不過近年來因為貪圖紅寶石(Ruby)的漂亮,所以同時也把它一併收到工具箱裡,當做自己謀生的工具之一。

也許你會好奇,Flash/AS寫得好好的,幹嘛學人家用什麼HTML5+JavaScript? 問的好! 簡單的答案是:因為我高興;複雜一點的答案是:我本來就喜歡一些冷門的玩具,而且AS的部份我覺得目前學的能力暫時夠混飯吃了,想來試點別的。雖然HTML5的規格都還沒標準化,瀏覽器的支援程度也都不太一樣,但如果要等到它真的紅的時候再進場,大概也輪不到我了,所以趁專案的空檔來研究一下,若學有所成的話,之後也可以試著引進到自己或客戶的專案裡。

Canvas是HTML5新進的東西,如果以Flash/AS的角度來看,它有點像是Bitmap或是BitmapData,不過操作起來可不像在AS裡那麼簡單。感謝我心目中的偶像 – Grant Skinner大神開發了一個叫做EaselJS的framework,可以讓Canvas的操作變得簡單許多,而且有Flash-like的API可以用,讓Flash/AS Developer可以比較無痛的上手。衝著這點以及GS大神的名號,於是選擇了它來當做我的第一個入門framework。

首先就要來面臨一個最現實的問題了:瀏覽器的支援程度(或該說是客戶電腦的瀏覽器是不是支援?)。這在Flash Player上幾乎沒這個困擾,但在HTML5就不是這麼回事了。目前比較新版本的瀏覽器,例如Firefox、Safari、Chrome以及Opera幾個大廠牌的支援都還算OK。不過我想那些不是重點,你我關注的還是那老兵不死的IE!! 版本新一點的IE也許還OK,但至於舊一點的.. 這個嘛,我想這部份的細節就讓大家自行體會了,對於這個問題,我只能說「I DON’T CARE」。

安裝

基本上不用什麼特別的安裝,到EaselJS的github repo上把壓縮檔下載回來,解壓縮後把lib資料夾裡的easel.js拿出來,放到適當的目錄就能用了。EaselJS是open source的,而且還是MIT授權,所以你可以放心的使用。在這個當下取得的release版本是0.3.2。

上路之前

在開始之前,建議最好對HTML以及JavaScript有些基礎的認識,不然可能容易迷路。接下來,在EaselJS裡有幾個主要的類別你也會需要花點時間了解一下:

DisplayObject體系

DisplayObject

寫過AS3的朋友應該不陌生這個名詞(如果覺得陌生的話,不要跟別人說你會寫AS3喔),它是所有可視物件(例如Bitmap、Shape、Text等)的最上層的父類別,定義一些基本屬性及方法,例如X、Y座標、放大縮小、旋轉、透明度等,更多細節請參閱文件DisplayObject章節

Stage

閃客們,有回家的感覺了嗎? 這個在AS3就是整個場景的最上層(root),但如果你從上往下看的話,也可以說是最下層的物件。Stage把Canvas包起來,然後在呼叫Stage的tick()update()的時候會render被包起來的Canvas。不過跟AS3不同的是,在AS3裡Stage只會有一個,在EaselJS可以有一個以上。細節請參閱文件Stage章節,或是看看底下的程式範例。

Container

這個在AS3是DisplayObjectContainer,在這邊名字變短了,但功能是差不多的,就是可以把一些DisplayObject包起來,以方便操作;當然你想要像在Flash裡面的一樣的一層包一層的巢狀結構也是可以的。在AS3裡常用的addChildaddChildAtremoveChildgetNumChildren等方法,以及碰撞偵測hitTest也都是在這個類別定義的。細節請見Container章節

Bitmap

拿來裝圖片跟影片用的,請參閱文件Bitmap章節

BitmapSequence

這個可能會比較陌生一些了,你可以暫時先把它想像成跟Flash的時間軸差不多的東西,這個類別定義了在Flash裡很常用的gotoAndPlay()gotoAndStop(),也許之後透過一些實作會更清楚它是幹嘛的。細節請參閱文件BitmapSequence章節

Shape

底下會提到的Graphics類別其實是沒辦法把向量圖形直接畫到Canvas上的,得透過Shape類別的包裝,並放到Stage上才行。細節請見文件Shape章節,或見底下的範例。

Text

看名字就知道它是用來處理文字的類別,請參閱文件Text章節

附上一個我自己畫的DisplayObject類別關係圖:

image

其它類別

Graphics

提供了方便的API,讓你可以畫一些向量圖形用的,這個類別還滿重要的,詳細內容請參閱文件Graphics章節

SpriteSheet

在Flash裡如果我們要做一個角色在跑步的MovieClip是很容易的,大概就是勞請設計師幫忙畫在跑步中的各個動作圖,然後使用關鍵影格,把各張圖片包在MovieClip就行了。這邊GS利用SpriteSheet來達到類似的功能,透過這個類別,你可以把一連串的圖片包成一個SpriteSheet來連續播放,看起來就有類似的動畫效果。細節請參閱文件SpriteSheet章節

Ticker

有點類似在Flash/AS裡ENTER_FRAME的東西,讓你可以在每個frame/interval做一些事情,細節請參閱Ticker章節

整體來看,EaselJS的類別不到20個,建議花點時間看一下文件或原始碼,如果你曾經或現在是個Flash Developer、自認AS3還算ok,也許EaselJS對你來說也不會太難上手的。 來寫code吧

講再多沒來個範例一定沒感覺的,來做個會旋轉的正方形好了(我知道這範例還滿無聊的,沒比Hello World強到哪裡去,but just for demo purpose):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <title>EaselJS Demo 001</title>
  <script type="text/javascript" src="easel.js"></script>
  <style type="text/css" media="screen">
    #my_canvas{
      background-color: gray;
    }
  </style>
  <script type="text/javascript">
    var stage;
    var box;
    var box_width = 50;

    function init()
    {
      var canvas = document.getElementById("my_canvas");
      stage = new Stage(canvas);

      var graphics = new Graphics();
      graphics.setStrokeStyle(3);
      graphics.beginStroke(Graphics.getRGB(255,255,255,0.8));
      graphics.drawRect(0, 0, box_width, box_width);

      box = new Shape(graphics);
      box.regX = box.regY = box_width / 2;
      box.x = canvas.width / 2;
      box.y = canvas.height / 2;

      stage.addChild(box);
      stage.update();

      Ticker.setFPS(30);
      Ticker.addListener(this);
    }

    function tick()
    {
      box.rotation += 4;
      stage.update();
    }
  </script>
</head>
<body onload="javascript:init();">
<canvas width="300" height="300" id="my_canvas"></canvas>
</body>
</html>

效果如下:

或按這裡檢視效果。

HTML的部份就不多解釋,JavaScript的部份則是把剛才下載回來的easel.js給引進來,然後在body的onload的地方呼叫自己寫的init()。不一定要這樣做,甚至你想把jQuery也引進來用也沒問題,要注意的是,因為這裡僅是做個示範,所以這個簡單的範例沒有特別檢查該瀏覽器是否支援Canvas。

上面這個範例需要說明的大概就是init()裡的那幾行程式碼了:

1
2
var canvas = document.getElementById("my_canvas");
stage = new Stage(canvas);

取得canvas,並把canvas用stage包起來。

1
2
3
4
var graphics = new Graphics();
graphics.setStrokeStyle(3);
graphics.beginStroke(Graphics.getRGB(255,255,255,0.8));
graphics.drawRect(0, 0, box_width, box_width);

利用Graphics類別把方塊畫出來,Graphics的method幾乎都是會回傳Graphics的instance回來,也就是說上面幾行例如setStrokeStyle()或是drawRect()等方法是可以直接串起來寫的。

1
2
3
4
box = new Shape(graphics);
box.regX = box.regY = box_width / 2;
box.x = canvas.width / 2;
box.y = canvas.height / 2;

因為Graphics畫完即使加到stage上,也還是看不到(因為它不是DisplayObject,請見上面的類別圖),所以你需要再用個Shape類別把它包起來,待會要把它放到stage裡。這裡的regX跟regY指的是這個shape的註冊點。如果你不知道註冊點是什麼,你可以試著把設定註冊點那行程式碼拿掉,再看看旋轉效果如何就知道了。

1
2
stage.addChild(box);
stage.update();

呼叫addChild()把剛剛建立的box加到stage上,再呼叫update()來把上面的可視物件給render到canvas上。只有在stage叫呼update()或tick()方法的時候,canvas才會重畫。事實上,當stage呼叫update()的時候,會呼叫所有被這個stage包起來的instance的tick()方法。

1
2
Ticker.setFPS(30);
Ticker.addListener(this);

利用Ticker來設定FPS(每秒30格),並加上listener。

1
2
3
4
5
function tick()
{
  box.rotation += 4;
  stage.update();
}

在每個interval把box轉動4度,然後重畫canvas。這個function一定要取名做tick()嗎? 別的名字不行嗎? 我們在前面請Ticker去監聽,Ticker在每個intereval就會去呼叫被監聽的物件上的tick()方法,所以你如果取做別的名字是沒用的。

如果你是AS3的閃客,應該不會覺得太難上手,如果不是,我想上面這程式也沒多難懂才是。

結論

  1. 目前EaselJS仍是alpha版的,即使是GS大神非常神,但還是不敢保證在使用過程中不會遇到些神奇的狀況。我個人覺得以入門來說,這套用起來算是不會太難,況且GS大神自己也用這套framework幫微軟做了款守城遊戲(Pirates Love Daisies),而且當初用的還只是0.1版的EaselJS。(我真的覺得像這樣可以吃自己做的狗食超棒!)
  2. 我沒有評估過或使用過市面上所有類似的framework,不確定在效能或功能上EaselJS是不是最佳選擇,不過目前看起來效能不至於到非常差,而且入門門檻對閃客來說也不算高。如果各位有能力,也可以直接到EaselJS的github repo去fork一份原始碼來研究看看,說不定也能幫忙貢獻一些心力。
  3. 目前網路上找得到的相關資料似乎不太多,所以官方文件將會是你最好的朋友,最好也跟原始碼做一下朋友。

之後應該還會再實做幾個例子,順便再來幾篇文章,來分享一些更進階的心得。

參考文件:

Comments