首先我們改寫一下昨天的格式,還記得昨天我們用到的是這樣的寫法:
cursorX+= distanceX / period;
cursorY+= distanceY / period;
當時的想法也很簡單,因為總共有90偵,因此將座標加上distance / period共90次,就會是完整的位移,那麼,換句話說,其實就是,每1偵加上1/90的距離,可以寫成這樣:
let linear = 1 / period;
cursorX+= linear * distanceX;
cursorY+= linear * distanceY;
// 位移 = 線性常數 * 距離
其實,這個線性常數linear等同時間函數的微分,將它積分後會剛好等於1
阿 突然講了很難懂的話
在上面的公式中,輸入值為距離和總偵數(distance, period),輸出值則是每一偵要進行的位移,在這個例子中,因為共有90偵,linear * 90就好剛好會等於1,也就是說,實際上我們想控制這個參數,讓它經過90偵後剛好等於1,因而從原點轉移到目的地,所以我們要做的是便是「操縱0到1的過程」。
昨天留的思考題,不知道有沒有人解出來了呢?有的扣個1留言一下XD,現在就讓我們解答吧,首先我們先從簡單的開始,以前國中學到一元二次方程式,最先學的那個不外乎就是Y=X^2吧?也就是最簡單的,這個方程式有一個特性,就是當X在0~1之間時,Y也是0~1之間,剛才說我們希望「輸出值的總和」等於1,因此只要控制X在0~1之間就可以了!
開始困惑的朋友們莫慌!讓我們直接代數字進去看看,在第一偵的時候timer為90,隨著timer的減少
period | timer | X | Y | 總位移量
------------- | ------------- | ------------- | -------------
90 | 90 | 0 | 0 | 0
90 | 45 | 0.5 | 0.25 | 總距離的1/4倍
90 | 30 | 0.666 | 0.444 | 總距離的4/9倍
90 | 0 | 1 | 1 | 總距離的1倍
這樣是不是就很好懂了呢?從一開始幾乎不動,到後面越來越快,最後到達目標位置。
如果沒看過這兩個名詞的話,可以參考這個裡面的文件附圖說明
其實,我們剛剛做的就是ease-in函數,不過跟典型的不太一樣,只是很相似的地步,但也是最好理解的,不過,現在的問題是,我們已經拿到這個方程式,也會算總位移了,那要怎麼取得「每一偵的位移」呢?答案很簡單,就是「拿下一偵的位移減這一偵的位移」:
寫成程式碼就是:
let easein = Math.pow(1 - (timer-1) / period, 2) - Math.pow(1 - timer / period, 2);
let cursorX+= easein * distanceX;
let cursorY+= easein * distanceY;
如何?換上這行代碼後,動畫效果是不是多了幾番風味呢?
那反過來說ease-out要怎麼做呢?其實有個偷吃步的做法,就是把ease-in顛倒過來,慢的變快的,快的變慢的,拿1去減掉這個範圍0~1的參數,就會從「0到1」反過來「1到0」,公式:
也就是:
let easeout = Math.pow(timer / period, 2) - Math.pow((timer-1) / period, 2);
let cursorX+= easeout * distanceX;
let cursorY+= easeout * distanceY;
那麼,我們就完成很基本的ease-in和ease-out拉!灑花~
接著我們可以進一步混和不同方程式,寫成像這樣的格式:
cursorX+= (0.5 * linear + 0.5 * easein) * distanceX;
cursorY+= (0.5 * linear + 0.5 * easein) * distanceY;
這樣可以使得easein的效果比較緩和,看起來不會那麼做作,不過要注意參數總和必須為1
function MouseAnime(){
if(!isPathing){
if(timer > 0){
let linear = 1/period;
let easeout = Math.pow(timer / period, 2)
- Math.pow((timer-1) / period, 2);
let easein = Math.pow(1 - (timer-1) / period, 2)
- Math.pow(1 - timer / period, 2);
let a = input.linear;
let b = input.easein;
let c = input.easeout;
cursorX+= (a * linear + b * easein + c * easeout) * distanceX;
cursorY+= (a * linear + b * easein + c * easeout) * distanceY;
timer--;
}
else{
cursorX+= (mouseX - cursorX) / 5;
cursorY+= (mouseY - cursorY) / 5;
}
}
let size = WIDTH * 0.1;
if(MouseImg.complete) context.drawImage(MouseImg,
cursorX-size/2, cursorY-size/2,
size, size);
}
透過abc參數,給予不同的權重,設計上希望a+b+c=1,不過為了方便直接設計權重比例,最後共同除以(a+b+c)就可以了
這樣混和的方法,也可以創造出新的方程式,比方說回彈機制等等的,
甚至XY可以分別採用不同的動畫模式,例如:X是純Linear、Y是純Ease-in,玩起來還是有Ease-in的味道在,只是相比之下沒有那麼浮誇;混合的方法則可以設計出迴旋鏢等等的騷操作。
我就把這份快樂留給大家試試看吧!可以拿這份架構去修改參數,或者加入更多其他的方程式
今天花了四個小時坐客運,時間被壓縮了不少,文章也都是親筆撰寫,也花了點時間設計架構,故內容寫少一點請見諒拉,但還是推薦大家可以去玩一下demo,會越來越精彩~明天要開始引入物件的觀念!