動畫的基本定義是隨著時間而變更影像所產生的動態效果或變化。在此樣本中,目標是要建立依垂直軸旋轉的球面月球效果。不過,為了要瞭解動畫,您可以忽略樣本內球面扭曲的部分。考量實際載入以及做為月球影像資料來源的影像:
如您所見,影像並不是一或多個球體,而是月球表面的矩形相片。由於相片正好是在月球的赤道上拍攝,因此靠近影像頂端和底端的影像部分便會遭到延伸及扭曲。為了移除影像的扭曲效果,並使其顯示為球面,我們將會使用一個置換對應濾鏡,此部分將於稍後說明。不過,由於來源影像為矩形,因此若要建立球體旋轉的效果,程式碼只需要水平滑動月球表面相片即可。
請注意,影像實際包含了兩張緊鄰著彼此的月球表面相片之副本。這個影像為來源影像,影像資料會為了建立動畫的外觀而重複複製此來源影像。兩個影像副本若緊鄰著彼此,便可以更輕鬆地建立一個連續、不中斷的捲動效果。讓我們來逐步進行動作的處理程序,以瞭解其運作方式。
這個程序實際上包含了兩個不同的 ActionScript 物件。首先,其中有一個載入的來源影像,在程式碼內是以名為
textureMap
的 BitmapData 實體來表示。如同先前的內容所提及,
textureMap
會在載入外部影像後,立即填入影像資料,當中使用的程式碼如下:
textureMap = event.target.content.bitmapData;
textureMap
的內容是矩形的月球影像。此外,為了建立旋轉的動畫,程式碼中會使用名為
sphere
的 Bitmap 實體,該實體是實際在螢幕上顯示月球影像的顯示物件。和
textureMap
一樣,在
imageLoadComplete()
方法內會建立
sphere
物件,並填入其初始的影像資料,其中使用了下列程式碼:
sphere = new Bitmap();
sphere.bitmapData = new BitmapData(textureMap.width / 2, textureMap.height);
sphere.bitmapData.copyPixels(textureMap,
new Rectangle(0, 0, sphere.width, sphere.height),
new Point(0, 0));
如程式碼中所示,
sphere
已初始化。其
bitmapData
屬性 (由
sphere
顯示的原始影像資料) 會建立與
textureMap
具有相同的高度和其一半的寬度。換句話說,
sphere
的內容將會是一個月球相片的大小 (因為
textureMap
影像內含兩個相連的月球相片)。接著,
bitmapData
屬性會使用其
copyPixels()
方法填入影像資料。
copyPixels()
方法呼叫內的參數會指定以下幾項作業:
-
第一個參數指定影像資料是從
textureMap
複製而來。
-
第二個參數 (即 new Rectangle 實體) 會指定要從
textureMap
的哪個部分拍攝影像快照。在此例中,該快照為起始自
textureMap
左上角的矩形 (由前兩個
Rectangle()
參數
0, 0
表示),而此矩形快照的寬度和高度都符合
sphere
的
width
和
height
屬性。
-
第三個參數 (即 x 和 y 值都為
0
的新 Point 實體) 會定義像素資料的目標。在此例中,即為
sphere.bitmapData
的左上角 (0, 0)。
以視覺化的方式呈現時,程式碼會從下列影像以外框框住的部分複製
textureMap
像素,並將它們貼到
sphere
中。換句話說,
sphere
的 BitmapData 內容即是此處強調的
textureMap
部分:
不過仍請記住,這只是
sphere
的初始狀態,也就是複製到
sphere
的第一個影像。
載入來源影像及建立
sphere
之後,由
imageLoadComplete()
方法執行的最後一項工作即是設定動畫。動畫由名為
rotationTimer
的 Timer 實體所驅動,這個實體是使用下列程式碼建立及啟動:
var rotationTimer:Timer = new Timer(15);
rotationTimer.addEventListener(TimerEvent.TIMER, rotateMoon);
rotationTimer.start();
程式碼會先建立名為
rotationTimer
的 Timer 實體,而傳遞給
Timer()
建構函式的參數表示
rotationTimer
應該每 15 毫秒就觸發其
timer
事件。接下來,則會呼叫
addEventListener()
方法,指定在
timer
事件 (
TimerEvent.TIMER
) 發生時呼叫
rotateMoon()
方法。最後,timer 實際上是由呼叫其
start()
方法所啟動的。
受到
rotationTimer
方法的定義方式之影響,因此大約每 15 毫秒 Flash Player 就會呼叫 MoonSphere 類別內的
rotateMoon()
方法,此處即是月球的動畫產生的地點。
rotateMoon()
方法的原始碼如下:
private function rotateMoon(event:TimerEvent):void
{
sourceX += 1;
if (sourceX > textureMap.width / 2)
{
sourceX = 0;
}
sphere.Data.copyPixels(textureMap,
new Rectangle(sourceX, 0, sphere.width, sphere.height),
new Point(0, 0));
event.updateAfterEvent();
}
程式碼會執行下列三項作業:
-
變數
sourceX
的值 (最初設定為 0) 會遞增 1。
sourceX += 1;
如您所見,
sourceX
會用來決定像素將被複製到
sphere
上的
textureMap
內的哪個位置,因此這個程式碼具有在
textureMap
上將矩形向右移動一個像素的效果。回到視覺化呈現的部分,在幾個動畫循環後,來源矩形將會向右移動數個像素,就像這樣:
在更多個循環後,矩形將會移動得更遠:
在那些複製之像素中,這個逐步、平緩的位置移動即是動畫的關鍵。藉由緩慢及連續地將來源位置向右移動,顯示在螢幕上的影像會以
sphere
顯示為連續向左滑動的情形。這就是為什麼來源影像 (
textureMap
) 需要使用兩個月球表面相片副本的原因。由於矩形會連續向右移動,因此大部分的時間它不會覆蓋在單一月球相片上,而是與兩張月球相片重疊。
-
隨著來源矩形緩慢地向右移動,將出現一個問題。最後,矩形將會到達
textureMap
的右邊緣,並且將會耗盡可複製到
sphere
上的月球相片像素:
接下來的程式碼將可解決此問題:
if (sourceX >= textureMap.width / 2)
{
sourceX = 0;
}
程式碼會檢查
sourceX
(矩形的左邊緣) 是否已到達
textureMap
的中間。如果是的話,便會將
sourceX
重設回 0,使其移回
textureMap
的左邊緣,並再次啟動循環:
-
有了適當計算的
sourceX
值,建立動畫的最後一個步驟便是實際將新來源矩形像素複製到
sphere
上。執行此動作的程式碼非常類似於最初填入的
sphere
(先前的內容中已提及);唯一的差別是,在此例中,在
new Rectangle()
建構函式呼叫內,矩形的左邊緣會放置在
sourceX
上:
sphere.bitmapData.copyPixels(textureMap,
new Rectangle(sourceX, 0, sphere.width, sphere.height),
new Point(0, 0));
請記得,每隔 15 毫秒就會重複呼叫此程式碼。隨著來源矩形的位置連續地移動,並將像素複製到
sphere
上,畫面上會顯示由
sphere
代表的月球相片影像連續滑入之效果。換句話說,月球看起來就像在連續旋轉。