無 library 開發視差滾動筆記

恩… 其實等於是自幹一個 library…


為什麼寫了是差滾動:視差滾動這兩年還滿流行的,一直很想寫一個來玩玩,但一直沒有機會,一個月前忍不住寫了一個我可能完全用不到的視差滾動 library,然後就一直擺著裝飾我的 Github ( XD),直到上個禮拜 Node Knockout 的時候隊友希望要一個視差滾動的宣傳首頁才把他拿出來試試。從中間學到了一些小技巧,這週也做了一些修正,這篇就是簡單紀錄一些最近的心得。


脫離 library 後需要自己解決的問題

  1. 該怎麼偵測網頁捲動?
  2. Transition
  3. 該怎麼讓套用 css 變簡單?(vendor-prefix)

該怎麼偵測頁面滾動?

  1. scroll event ?
  2. setTimeout ?
  3. requestAnimationFrame !!

在 window 上監聽 scroll event?基本上不可行,scroll event 被呼叫的太過頻繁,網頁會變很頓,既然如此那我們就要想辦法避開這個問題,最直覺的解法就是 setTimeout/setInterval,但是這還是不夠完美,依然會造成過度重繪問題。比較好的作法是使用 requestAnimationFrame,使用 requestAnimationFrame 後瀏覽器會依據這個網頁是不是當前 tab 等資訊來作適當的資源分配,網頁會非常滑順!詳細的規格可以閱讀 w3c 文件mdn,遇到較舊瀏覽器的時候才使用 setTimeout。

var requestAnimationFrame = (function () {
return window.requestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.msRequestAnimationFrame
|| setTimeout;
})();

還可以搭配 scroll event 來判斷是否要啟動 loop,當在同一個位置停留兩次後暫停 loop(cancelAnimationFrame),捲動事件觸發後開始 loop。

Transition

transition 也是一個問題,要先了解自己的需要,像是我預期我自己大部分情況下網頁會有 ”區塊性”,每個區塊通常只表達一件事,區塊間的關連性可能也不高,所以我的想法就是把網頁分成數個區塊,然後獨立處理自己的 Transition,依據這個想法,當我在區塊一的時候我知需要知道我在區塊一的瀏覽進度,而且不需要精準到 px,其他區塊也完全可以忽略,不需要更新他們內部任何 element 的 css。其實依照我目前個人的觀察,這種作法應該可以解決 80% 以上的是差滾動網頁所需要辦到的事情,因為很少會出現一個跨區塊的物件,就算出現也可以接力完成,而且個人覺得這樣在寫程式時也比較方便,可以一眼知道目前畫面有哪一些東西會有動作。

該怎麼讓套用 css 變簡單?

我想我對簡單的定義是說我不打 prefix,也不要把 css 寫在 html,我要看到真實的程式碼,但是又有比較統一的寫法,有點囉唆 XD。

這是我的預期寫法

section.transitions([
{key: 'opacity', start: 0, end: 50, from: 0, to: 1, format: '%s'}
]);

給一個 css 屬性名稱然後設定他從幾 % ~ 幾 % 要處理這個 transition,還有開始與結束的值,format 是用來設定最後 css 屬性值的格式用的,主要是解決不同單位還有 transform 的問題,transform 寫法如下,values 裡面所計算出來的值案順序對應後面的 %s。

{key: 'transform', prefix: true, start: 0, end: 50, values: [from: 0, to: 90}, {from: 1, to: 1.5}], format: 'rotateX(%sdeg) scale(%s)'}

思考完該讓 api 長什麼樣子之後接著就是實作,這裡主要會遇到的問題是 prefix,如果把所有主流瀏覽器的 prefix 全部加上感覺真的很蠢,應該是網頁載入後我們去偵測瀏覽器 prefix 然後每次更新 css 的時候只加這個 prefix,那要怎麼偵測呢?我們會到 getComputedStyle 這個 function 來取得所有的 css 屬性名稱還有值,他取得的資料是真的套用於被傳入的 element 上的 css,包含瀏覽器預設,所以通常都可以看到我們所需要的 prefix 在裡面,下面這是我的作法。

var style = window.getComputedStyle(document.documentElement);
var match = Array.prototype.join.call(style, '').match(/-(?:o|moz|webkit|ms)-/i);
var prefix = match && match[0];

搞定!

補充

https://github.com/poying/sections.js

不想自幹的話就來用我寫的吧…
文件裡面看到 bj4 的時候不用驚訝,那就是我懶得打 XD

Email me when Po-Ying Chen publishes or recommends stories