在 window 物件當中,提供了 scrollTo(x, y)
的方法,我們可以透過 scrollTo
來操作滾動。但, scrollTo 方法是直接讓滾動軸移到指定的位置,並沒有動畫的效果,如果要藉由動畫的幫助達到良好的使用者體驗,我們必須自己設計。
基本動畫知識
在開始之前先來讓我們了解一下動畫的基礎吧!
動畫最基本的要素有這些:
- 時間 = 距離(位移)/ 速度
- 位移 = 速度 * 時間
- 速度 = 距離 / 時間
有了這些基礎知識就可以來製作簡單的動畫效果了。
第一次嘗試
我們要做的事是要讓 scroll 能夠用動畫的方式滑到自己想要的地方。所以,在本例當中,scrollTo(x, y)
將會是我們的位移
。速度的話,我們先暫定是 200ms 吧!
|
|
咦?這個 t 是 undefined
吧?
在定義 t(time) 之前,我們先來思考 t 應該要是什麼。根據剛剛的公式,時間 = 距離 / 速度,所以在本例當中,t 為 targetY - scrollY / speed
。這樣寫的話如果 scrollY 大於 targetY 的話時間就會為負了,所以這邊我們要取絕對值。
|
|
到目前為止,我們的 scroll 動畫雛形已經出來,不過存在一些問題:
- 距離太遠的時候,動畫的時間顯得有點長
- 這個動畫不會停
現在我們來改善一下 scroll 的動畫。
距離太遠
顯然如果距離太遠時,動畫完成的時間會變得更長,所以我們需要限制一下 t 的範圍。
|
|
這樣子好多了,在距離太遠時,動畫不會顯得太慢。
動畫不會停
在程式碼當中,因為沒有設定停止條件,所以會無止盡的延續下去。芝諾悖論
怎樣才算是完成了呢?這邊的終止條件是目前的時間(禎數)等於 t 的時候,就算終止了。
|
|
這邊我們將 setTimeout
取代成 requestAnimationFrame
,requestAnimation 跟 setTimeout 的差別在於使用 requestAnimationFrame
時,瀏覽器會幫我們做最佳化,在不必要的時候不會進行重繪,達到節省資源的效果。
目前主流瀏覽器都已經支援了(主流當然不包含 IE8 囉!)
但是,為什麼感覺動畫那麼不自然呢?
有沒有發現,時間與距離是完全呈現線性變化的,這代表我們假設這個物體在所有的時間點,速度都是相同的。真實生活中通常不會有這樣的事情發生,物體一定都是從靜止狀態逐漸加速,再從移動的狀態中逐漸停止。而上一篇的範例當中,動畫是突然開始,突然停止。在現實生活中,物體的移動速度並非成線性變化,這是造成我們動畫看起來不自然的主要原因。
知道了原理之後,就可以馬上來實作了:
- 計算距離
- 重新計算每一次的位移 = 距離 * 比例係數(easing)
把我們原本的 function
改寫成這樣:
|
|
這邊我們加上了一個簡單的 cos 函數,重新計算位移的位置,達到 easing 的效果。詳細 easing 效果,可以到 easing.js 看看,這裡蒐藏了很多 ease
效果。
結論
這邊很簡略地用 scroll 的 API 當作範例,介紹了動畫的基礎。當然還動畫的原理跟使用,又是另外一門深奧的學問了。
到目前爲止,我們的 scroll 動畫就算完成了,但因為位移是線性移動,看起來比較不自然一點,下一篇文章,再來介紹 ease 的概念,讓我們 scroller 變得更 smooth。
原本以為篇幅很長,不過基本的動畫原理的確只有這樣而已,加上了 easing 之後,動畫的效果看起來自然很多,當然,你也可以依照自己的經驗調整參數,達到更完美的使用者體驗。
不過,如果可以,盡量不要綁架使用者預設的滾動效果,一來可能會造成效能的問題,二來你綁定的滾動效果如果沒有依照使用者預期反應的話,很容易造成非常差的體驗。