這是〈Svelte 從小白到入門〉系列的第八集,各集連結如下:
- 〈Svelte 從小白到入門(一)元件、事件、Reactivity〉
- 〈Svelte 從小白到入門(二)Props、邏輯、迴圈、Await〉
- 〈Svelte 從小白到入門(三)事件〉
- 〈Svelte 從小白到入門(四)數據綁定〉
- 〈Svelte 從小白到入門(五)生命週期〉
- 〈Svelte 從小白到入門(六)Store〉
- 〈Svelte 從小白到入門(七)動態效果〉
- 〈Svelte 從小白到入門(八)進出場效果〉
- 〈Svelte 從小白到入門(九)動畫效果〉
- 〈Svelte 從小白到入門(十)Action〉
- 〈Svelte 從小白到入門(十一)CSS Class〉
- 〈Svelte 從小白到入門(十二)Slot〉
- 〈Svelte 從小白到入門(十三)Context〉
- 〈Svelte 從小白到入門(十四)Svelte 的特殊元素〉
- 〈Svelte 從小白到入門(十五)Module Context〉
- 〈Svelte 從小白到入門(十六)Debugging〉
- 〈Svelte 從小白到入門(十七)結束與展望〉
進出場效果
進出場效果指的是 HTML 元素在出現或消失時的微互動效果,常見的有淡入、淡出、滑進、滑出等,這些效果都是原生 CSS 與 JS 的再封裝,由 Svelte 開發團隊改造成在 Svelte 元件內的語法。
淡入淡出
最基本的用法,在 JS 引入 fade
,在 HTML 標籤內以 transition:fade
的形式使用:
在此之前我們見過 on:
、bind:
,現在這類的語句又出現了新的 transition:
,應該見怪不怪了…吧?
效果參數
進出場效果也可以配置參數,下面的例子是飛進飛出:
我們用 transition:fly="{}"
裡面放個物件 { y: 200, duration: 2000 }
做為 fly 效果的配置參數。
如同上一集的 motion,即使文字飛到一半就切換狀態,Svelte 也會幫我們立即反應,不用等到第一段全部飛完才跑第二段的效果。
進場、出場不同效果
用 transition:
會使進場、出場同效果,如果想追求更虛華的進出場不同效果,那可以改用 in:
與 out:
。
合併前兩個例子:
飛入淡出,夠虛華!
自定義 CSS 效果
如果追求更虛華的效果,也可以自製效果,自製效果的函式,必須接受至少一個 node
參數,表示效果要應用的 HTML 元素,以及回傳一個效果物件,這個回傳的物件可以有這些成員:
delay
— milliseconds before the transition beginsduration
— length of the transition in millisecondseasing
— ap => t
easing function (see the chapter on tweening)css
— a(t, u) => css
function, whereu === 1 - t
tick
— a(t, u) => {...}
function that has some effect on the node
以上太高深不解釋,實際看下面的例子:
上面的 spin()
的回傳值內的 css
基本上是一系列 CSS 樣式,而這些樣式的值隨著 elasticOut(t)
而變,t
則是一個隨時間變化的變數,效果發生時為 0
,效果結束時為 1
,而效果發生途中 t
的值則由 easing
變數而定,總的來說這就是一個時間函數,而 t
嚴格的說應該是 tick
,但簡單的理解 t
就是時間。
自定義 JS 效果
某些效果難以用 CSS 實現,那麼可以以 JS 實現:
效果觸發事件
這些進出場效果作用的同時,也會觸發事件,我們可以用 on:
的語法來呼叫效果發生或結束時要執行的函式。
與效果相關的事件如下:
introstart
outrostart
introend
outroend
應該可以望文生義。
範例如下:
區域效果
在下面的範例中,slide
是寫在最內層的 <div>
內,然而在 Svelte 的設計上,外層的容器一旦狀態改變,也會引發內層的 <div>
的 slide
的效果,所以當我們對「show list」做切換,那些 one、two、three 等的物件也都會產生滑動,如果不想要這樣,我們得為效果加上 local
這個修飾器,這個修飾器限定了效果只會受元素本身的容器觸發:
Crossfade
Crossfade 華文也叫淡入淡出,是指元素從 A 處淡出,並且在 B 處淡入。
下面的 to-do list 範例裡,當左邊「todo」內的工項被打勾,就會移動到右邊的「done」內,反之亦然:
在程式邏輯方面,所有的工項被存放在一個 todos
的陣列裡,當工項的 checkbox 被切換,會觸發 mark()
改變該工項是否已完成的記號 done
,後面呼叫的 remove()
與 concat()
則分別把該工項從 todo
內移除,及再放回陣列內成為最後一個成員。
在效果方面我們引入了 crossfade()
函式,它有兩個效果可用,send
與 receive
,對左邊的 todo 內的工項來說,當它被打勾時,整個 todo 區的子陣列發生改變,會觸發該工項的 out:send={{key: todo.id}}
,而在右邊的 done 的子陣列也會發生改變,觸發該工項的 in:receive={{key: todo.id}}
,此時 out:send
的這邊會移動並淡出,in:receive
的這邊也會移動並淡入,最後給予用戶的感受是無縫且絲滑的操作體驗。
上面的 send
與 receive
都接受 {key: todo.id}
這物件當作參數,這裡的 key
運用到了第二集提過的「迴圈加 key」的技巧,讓不管是 todo 區或是 done 區,工項的切來切去都可以使 crossfade 效果應用在正確的對象上。
Crossfade 的 Fallback 函式
把上面的範例再弄複雜一點,工項除了移來移去外,用戶應該還可以加新工項及刪工項,添加與刪除並沒有「A 處移到 B 處」的情境,因此 send
、receive
也無從發揮,當 send
或 receive
找不到目的地時,只要在 crossfade()
函式內做一個 fallback()
函式,就會調用那個 fallback()
:
Key 區塊
前面的進出場效果都是以 HTML 元素的狀態發生改變為基礎,若是想以 HTML 的內容的變化而觸發進出場效果,那得利用 key 區塊:
在上面的示例中,按鈕只會改變 number
的值,不影響 <p>
,因此我們在 {number}
包一層 <span in:fly="{{ y: -20 }}">
用於聲明進場效果及參數,並在更外層包覆 {#key number}
用於聲明當區塊內的 {number}
改變時,要觸發那 fly 進場效果。
小結
整理一下截至目前為止 Svelte 在 HTML 標籤內出現過的幾種聲明式語句:
on:
:綁定事件與函式bind:
:綁定數據transition:
:聲明進出場效果in:
:聲明進場效果out:
:聲明出場效果
本集重點整理:
- 對於效果,我們可以用
local
修飾器限定效果的作用域只針對該效果本身的的容器反映,而不會受到更外層的容器觸發 crossfade()
及相關的send
、receive
效果可以幫我們做到「從 A 處淡出,往 B 處淡入」的效果- 本集也認識了 HTML 內的新語句
{#key}
區塊,參見最後一個範例{#key number}
,當{number}
發生變化時,觸發區塊內的進出場效果。