這是〈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 begins
  • duration — length of the transition in milliseconds
  • easing — a p => t easing function (see the chapter on tweening)
  • css — a (t, u) => css function, where u === 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() 函式,它有兩個效果可用,sendreceive,對左邊的 todo 內的工項來說,當它被打勾時,整個 todo 區的子陣列發生改變,會觸發該工項的 out:send={{key: todo.id}},而在右邊的 done 的子陣列也會發生改變,觸發該工項的 in:receive={{key: todo.id}},此時 out:send 的這邊會移動並淡出,in:receive 的這邊也會移動並淡入,最後給予用戶的感受是無縫且絲滑的操作體驗。

上面的 sendreceive 都接受 {key: todo.id} 這物件當作參數,這裡的 key 運用到了第二集提過的「迴圈加 key」的技巧,讓不管是 todo 區或是 done 區,工項的切來切去都可以使 crossfade 效果應用在正確的對象上。

Crossfade 的 Fallback 函式

把上面的範例再弄複雜一點,工項除了移來移去外,用戶應該還可以加新工項及刪工項,添加與刪除並沒有「A 處移到 B 處」的情境,因此 sendreceive 也無從發揮,當 sendreceive 找不到目的地時,只要在 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::聲明出場效果

本集重點整理:

  1. 對於效果,我們可以用 local 修飾器限定效果的作用域只針對該效果本身的的容器反映,而不會受到更外層的容器觸發
  2. crossfade() 及相關的 sendreceive 效果可以幫我們做到「從 A 處淡出,往 B 處淡入」的效果
  3. 本集也認識了 HTML 內的新語句 {#key} 區塊,參見最後一個範例 {#key number},當 {number} 發生變化時,觸發區塊內的進出場效果。