tweened
tweened
tweened
實作動態漸變色票 我們先前花了三天的時間好好介紹了 Svelte 的 Store,為了要能夠讓資料有水平溝通的管道,或者為了讓一般 Javascript 的變數,能夠跟 Svelte 元件當中的變數有溝通的橋樑,Svelte 提供了 Store 這個方便的工具。我們不僅可以使用 Svelte 打造的最基本的 writable
Store,同時還可以從 writable
Store 出發,快速的客製化我們需要的 Store。
以為 Svelte 只提供這麼樸素的 Store 嗎?在看過昨天第 27 天:Svelte 的過場:transition
的文章之後,應該有發現到 Svelte 其實是個很花俏的傢伙了吧 😂 所以說,除了最基本的 writable
Store 之外,Svelte 還另外提供了一個有趣的東西:具有動態效果的 Store。今天要介紹的,就是這個有趣的動態效果 Store:tweened
。
tweened
首先我們可以到 Svelte 的 REPL (Read-Eval-Print-Loop) 寫一段程式碼:
<script>
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
const progress = writable(0);
onMount(() => {
const interval = setInterval(() => $progress === 1? progress.set(0): progress.set(1), 1000)
return () => clearInterval(interval);
})
</script>
<progress value={$progress} />
第四行:const progress = writable(0);
初始化一個 writable
Store,命名為 progress
。
第七行:const interval = setInterval(() => $progress === 1? progress.set(0): progress.set(1), 1000)
將 setInterval
放進 onMount
當中,並利用 setInterval
的功能,讓 progress
一秒一秒從 0
跳到 1
,再從 1
跳回 0
。
第十二行:<progress value={$progress} />
並讓 progress
當中儲存變數的變化反應到 HTML 元素 <progress>
上。
圖一、我跳到 1
了!我跳回 0
了!我又跳到 1
了!我又跳回 0
了!
可以這樣跳來跳去固然有趣,但是只有非 0
即 1
的 <progress>
似乎稍嫌呆板。讓我們試試看具有動態效果的 Store tweened
看看吧:
<script>
import { onMount } from 'svelte';
import { tweened } from 'svelte/motion';
const progress = tweened(0);
onMount(() => {
const interval = setInterval(() => $progress === 1? progress.set(0): progress.set(1), 1000)
return () => clearInterval(interval);
})
</script>
<progress value={$progress} />
第三行:import { tweened } from 'svelte/motion';
引入 tweened
。
第四行:const progress = tweened(0);
因為 tweened
其實也是一種 Store,使用方法就像 Store。首先初始化一個 tweened
,並將其命名為 progress
。
第七行:const interval = setInterval(() => $progress === 1? progress.set(0): progress.set(1), 1000)
因為 tweened
也是一種 Store,所以也可以用 $progress
來取得 progress
當中儲存的變數的值,並且用 set
這個方法來改變儲存的變數。
第十二行:<progress value={$progress} />
在 HTML 當中也可以展開 Javascript,並且用 $progress
拿到變數的值。
圖二、看我伸縮自在的 tweened
!
是不是可愛多了呢?tweened
可以在前值根後值之間做出補間的效果,藉此讓使用者互動介面的表現能力更強。既然 tweened
有補間的效果,想必我們也可以使用參數來改變補間的變化方式囉。沒錯,tweened
提供四個參數讓我們可以對補間效果做更自由的調整,這幾個參數包括:
delay
:補間效果開始的時間,單位是毫秒。duration
:補間效果持續的時間,單位同樣是毫秒。easing
:補間效果的變化方式,就跟昨天第 27 天:Svelte 的過場:transition
介紹過的 easing
是相同的意思。interpolate
:補間的定義方式。除了數字之外,藉由 interpolate
,只要定義得當,我們也可以對文字、陣列、或是色碼做出補間的效果。 看到了嗎?色碼也可以用 tweened
來做出補間的效果,是不是很棒呢。今天就讓我們用新學的動態效果 tweened
來替我們的色票產生器做出更出色的變化吧!
tweened
實作動態漸變色票我們的色票產生器可以隨機產生不同色碼的色票,今天想要實作的效果,就是在色碼重新進行隨機生成的時候,加入色碼補間的效果,讓色票是逐漸地從原本的顏色變成新的顏色。用文字說明太抽象的話,先來實際來看個成果吧:
圖三、令人著迷的漸變色票🎉
在開始動手前,讓我們先把 Palettes.svelte
重新整理一下:
/src/lib/Palettes.svelte
<script>
import Palette from "./Palette.svelte";
export let palettes;
</script>
<div class="palettes">
{#each palettes as { hex, locked, id } (id)}
<Palette bind:hex bind:locked />
{:else}
<div class="card no-color">
<div>
<p>No color to show. Please add a color.</p>
</div>
</div>
{/each}
</div>
第二行:import Palette from "./Palette.svelte";
我們想要把 each
段落當中迭代的一張一張色票提取出來,變成新的 Svelte 元件 Palette.svelte
。所以這邊先說我們要引入 Palette
這個 Svelte 元件,等等我們在來詳細把 Palette
實作出來。
第八行:<Palette bind:hex bind:locked />
引入 Palette
之後,就可以在 each
邏輯區塊當中簡簡單單的寫這麼一行,其他詳細的內容就可以放在 Palette
這個 Svelte 元件裡頭。記得要把 hex
跟 locked
都用 bind
綁定起來。
接著來實作 Palette.svelte
:
/src/lib/Palette.svelte
<script>
import unlock from "../assets/unlock.svg";
import lock from "../assets/lock.svg";
import { toastStore } from "./toastStore";
export let hex;
export let locked;
const getHexName = async (hex) => {
const url = `https://www.thecolorapi.com/id?hex=${hex}`;
const res = await fetch(url);
const json = await res.json();
return json.name.value;
};
const handleLock = ({ locked, hex }) =>
toastStore.addToast({ action: locked ? "lock" : "unlock", hex });
</script>
<div class="card">
<div class="palette" style="background: #{hex}" />
<div class="hex-code">
<input type="text" size="10" bind:value={hex} />
<p>
{#await getHexName(hex)}
wait...
{:then name}
{name}
{/await}
</p>
</div>
<!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
<div class="lock-icon">
<label>
<input
type="checkbox"
bind:checked={locked}
on:change={() => handleLock({ locked, hex })}
/>
{#if locked}
<img src={lock} alt="color-locked" />
{:else}
<img src={unlock} alt="color-unlocked" />
{/if}
</label>
</div>
</div>
第五行:export let hex;
記得要將 hex
用 export
這個關鍵字使其變成 Palette
這個 Svelte 元件的 Property。
第六行:export let locked;
記得要將 locked
用 export
這個關鍵字使其變成 Palette
這個 Svelte 元件的 Property。
剩下的程式碼沒有太大改變就不贅述了。如此一來就完成將 Palettes.svelte
當中代表一張一張色票的程式碼獨立出來的動作了。接下來我們就來客製化一下 tweened
,做出可以對色碼進行補間的 tweened
。首先建立一個新的檔案 hexStore.js
:
/src/lib/hexStore.js
import { tweened } from "svelte/motion";
export const createHexStore = (hex) => {
const hexStore = tweened(hex, {
duration: 500,
interpolate: (a, b) => (t) => {
const rA = parseInt(a.slice(0, 2), 16);
const gA = parseInt(a.slice(2, 4), 16);
const bA = parseInt(a.slice(4, 6), 16);
const rB = parseInt(b.slice(0, 2), 16);
const gB = parseInt(b.slice(2, 4), 16);
const bB = parseInt(b.slice(4, 6), 16);
const rT = ("0" + Math.round((rB - rA) * t + rA).toString(16)).slice(-2);
const gT = ("0" + Math.round((gB - gA) * t + gA).toString(16)).slice(-2);
const bT = ("0" + Math.round((bB - bA) * t + bA).toString(16)).slice(-2);
return rT + gT + bT;
},
});
return hexStore;
}
第一行:import { tweened } from "svelte/motion";
引入 tweened
。
第三行:export const createHexStore = (hex) => {
宣告一個函式 createHexStore
。這個函式會吃進一個參數 hex
,並回傳可以對色碼進行補間的 tweened
。
第四行:const hexStore = tweened(hex, {
初始化一個 tweened
。
第五行:duration: 500,
設定 tweened
補間的變化全程時間為 500
毫秒。
第六行:interpolate: (a, b) => (t) => {
這就是客製化 tweened
讓 tweened
能夠對色碼進行補間的重點。interpolate
是一個函式,接受代表起始值 a
跟結束值 b
的參數,然後回傳一個補間函式,也就是時間 t
的時候應該呈現的補間的值。舉例來說,如果起始值是 1
,結束值是 0
,補間為線性變化,那麼時間進行了 0.5
時,補間函式應該要能算出補間的值為 0.5
。用色碼來做解釋的話,如果起始色碼是 rgb(0, 0, 0)
,結束色碼是 rgb(255, 255, 255)
,同樣讓補間線性變化,那麼時間是 0.5
時,補間函式應該要能算出補間色碼為 rgb(127.5, 127.5, 127.5)
。不過色碼不允許小數點,所以還要做個 Math.round()
四捨五入一下。
這樣一來就完成我們客製化的 tweened
了。回頭看看前面 tweened
應用的例子,其實我們想要做的動作就是用 tweened
儲存色碼的資料,並顯示在 <div class="palette">
這個 HTML 元素上。現在已經有了可以對色碼做出補間的 tweened
了,讓我們把這個厲害的 tweened
放進代表一張色碼的 Palette.svelte
當中:
/src/lib/Palette.svelte
<script>
/* 省略無關的程式碼 */
import { createHexStore } from "./hexStore";
const hexStore = createHexStore(hex);
$: hexStore.set(hex);
</script>
<div class="card">
<div class="palette" style="background: #{$hexStore}" />
<div class="hex-code">
<!—省略無關的程式碼 -->
</div>
<div class="lock-icon">
<!—省略無關的程式碼 -->
</div>
</div>
第三行:import { createHexStore } from "./hexStore";
引入 createHexStore
。
第五行:const hexStore = createHexStore(hex);
我們用 createHexStore(hex)
初始化一個客製化的 tweened
。
第六行:$: hexStore.set(hex);
用第 09 天:Svelte 中的 Javascript:陳述介紹過的互動陳述 $:
,如果 hex
發生變化,那就一起更新我們的 hexStore
。記得 hexStore
本質上就是 tweened
,就是一種 Store,所以可以用 hexStore.set
來改變儲存在裡面的變數。
第十行:<div class="palette" style="background: #{$hexStore}" />
並且在 <div class="palette">
當中將 background
設定成 $hexStore
,利用自動訂閱 (auto-subscription) 的方式,直接取得 hexStore
當中的值。
圖四、再依次看著令人著迷的漸變色票🎉
今天關於 Svelte 動態效果 tweened
的介紹就到這邊了。完整的程式碼可以在 Github 資料庫當中找到,那麼謝謝大家的閱讀!