其實網路上有很多相關的文章,都可以帶你更深入JS時,但常常問題在於,他們的舉例都不夠實際,並不是說不好,而是「需求的問題」,如果你和我一樣,在早期只是單純自我學習,而不是靠JS養活自己,那麼很容易沒有方向,即使那些文章說的頭頭是道,仍然很無感,因為平常根本沒有這樣的用途!
而這次的系列文,透過一個很明確的目標,帶大家實作,就是為了讓大家有感,從一開始造輪子,漸漸會發現大大小小的輪子散落四方,開始越來越難整理,今天便是一個分歧點,當我們要進入複雜的programming,單純的變數命名已經不符合我們的需求,那我們需求是什麼呢?我們要為遊戲做動畫,我們要讓一個東西動起來,還要讓無數個東西動起來,那要怎麼用有限的程式碼,實現無限個動畫呢?讓我們從第二章節娓娓道來:
首先,還是先從實作面開始,就從一個落葉解釋為什麼需要物件吧!要做一個"完整"的落葉動畫效果,你覺得要至少準備幾個參數呢?以下讓我們實際演示一遍
首先我們定義原點:
let originX;
let originY;
接著我們定義初始時間、動畫總生命週期:
let timestamp;
let lifeCycle; // 單位:毫秒(s)
然後設計落葉的初始角度、角速度和大小:
let rotateTheta; // 單位:rad
let rotateOmega; // 單位:rad/s
let size;
以為這樣就結束了嗎?不只這些,待會還要用到兩個方程式:
一般來說,為了得到路徑方程式,需要用速度對時間做積分:
但是這樣的系統太複雜,說好要跳過微積分的!我們可以簡化它,直接思考落葉是怎麼動的,印象中的葉子會左右擺動,緩慢飄弱,是不是很像鐘擺的往復運動呢?這種周而復始的循環,用到的是三角函數,其最簡單的形式是簡諧運動,也就是說,只需要有公轉角度、公轉速度:
let revolveTheta; // 單位:rad
let revolveOmega; // 單位:rad/s
我們就可以跳過以上複雜的方程式,繼續用國中數學畫圖了!
好了,材料備好了,用昨天的Demo改寫,實作看看吧
canvas.addEventListener('click', SetMouse);
function SetMouse(e){
originX = (e.pageX - Rect.left) * RATIO;
originY = (e.pageY - Rect.top) * RATIO;
size = 200;
rotateTheta = 0 / 180 * Math.PI;
rotateOmega = 40 / 180 * Math.PI; // 每秒旋轉40度
revolveTheta = 0 / 180 * Math.PI;
revolveOmega = 90 / 180 * Math.PI; // 每秒公轉90度
timestamp = Date.now();
lifeCycle = 6; // 生命週期6秒
}
幫大家回憶一下,Rect是用canvas.getBoundingClientRect()原生方法取得畫布在畫面中位置
let deltaTS = (Date.now() - timestamp) / 1000;
if(lifeCycle > deltaTS){
Clear(context);
// 第三步 + 第四步寫在這裡面
}
Clear是接續前次設計的清除畫布函式
let rotateNow = rotateTheta + rotateOmega * deltaTS;
let revolveNow = revolveTheta + revolveOmega * deltaTS;
cursorX = originX + 500 * Math.sin(revolveNow);
cursorY = originY + 200 * Math.sin(1.5 * revolveNow)
+ 100 * deltaTS;
設計讓落葉水平來回搖擺、垂直落下則會遇到微風的影響,一下快一下慢
if(leafImg.complete){
let width = size;
let height = size * (leafImg.height / leafImg.width);
context.save();
context.translate(cursorX, cursorY);
context.rotate(rotateNow);
context.drawImage(leafImg, -width/2, -height/2, width, height);
context.restore();
}
這樣總算是完成一個落葉動畫了!請看Demo
一開始你猜測會用到幾個變數呢?除了一開始定義的9個變數,還有途中計算的當前時間等等參數,至少就用到了12個變數欸!更不用說原本就定義好的cursorX和cursorY以及其他我們事先準備好的架構,難道沒有一個更好的方式去整理嗎?
沒錯,答案就是物件XD,而現在的狀況是,這個落葉的參數四散各處,我們想要的其實就是一種「可以將這個落葉視為一個整體的方法」,明天就帶大家一起反向思考,我們先談我們想要什麼,再談物件是什麼,你就會發現,哇!JS的物件真的是設計得太好了,直接給JS的創始人一個大大的讚