為何要用 React-Spring v9 ?

狀態機驅動 & 創造自然互動

在等待了整整一年之後 v9 終於進入 rc3 了

React-Spring 解決的問題

過去主流動的 Lib 像是 GSAP, Anime.js... 等動畫 lib 主要都是為了製作連續性複雜「動畫」而被設計出來的,所以當應用在 UI 互動甚至連續性操作的即時動態反饋,在 API 設計上往往並不是直接的滿足需求。

往往必須自己去做各種狀態對應的串接或中斷,導致開發上十分的不直感。

於是在這個以狀態機作為主流驅動的世界, 既然 UI 本身都是配合狀態在連動變化,那理所當然 UI 動畫也該跟著狀態補間連動才是。

Toggle Checkbox:
...
import { a, useSpring } from "react-spring"

const ReactSpringCheckboxToggleDemo = ({ children }) => {
    const [isOn, toggle] = useToggle(false)

    // style 類似 react 的 state, 但不會觸發 react dom 的連帶更新,
    // 而是自己獨立控管的樣式狀態機,來跟下面 a.{html_elements} 做連動狀態更新
    const style = useSpring({ x: isOn ? 100 : 0 })

    return (
        <>
            <input type="checkbox" value={isOn} onClick={toggle} />
            <a.div style={style} className="ball">
              {`<=`}
            </a.div>
        <>
    )
}

上面談到的是 react-spring 狀態機所解決的問題,接著再來看另一個情境。

那就是使用者在「即時性操做」的「連續動態」上,舊有動畫庫的設計形式往往難以直接達成「自然的」反饋。

何謂自然?

bring your components to "life" with simple spring animation primitives

讓使用者能對虛擬世界的操作與現實有所連結

簡單來說就是讓使用者自然界中熟悉的物理性帶入操作互動之中。

spring

創造物理性的幾個關鍵參數

  • mass:質量
  • tension:張力
  • friction:阻力、摩擦力
  • velocity:速度

一般動畫庫多是用設定 ease + duration 的形式來調整動畫運作的形式,但 React-Spring 則是用上面的物理性參數來實現動態。 也就是說你不再是決定動畫運作的時間。

以上面的彈簧運動來說,在過去或許會用 repeat infinite 配上 ease-out 的形式來實現。

但也就相對難以進一步表現出物質界自然的衰減性。

Click
// React-Spring v9
const ReactSpringDemo1 = ({ children }) => {
    const [{ x }, animate] = useSpring(
        {
            x: 0,
            config: {
                mess: 2, // 質量
                friction: 1, // 阻力
                tension: 200, // 張力
            },
        },
        [] // 傳一個 [] 是 v9 拿 setSpring 的新形式,也可以直接對 useSpring 傳入 function 的形式獲得
    )

    const handleAddInitVelocity = useCallback(() => {
        x.stop() // 想藉由 velocity 為觸發給予動量必須先停止才能作用
        animate({
            x: 0,
            config: { velocity: 1 },
        })
    }, [])

    return (
        <a.div style={{ x }} onClick={handleAddInitVelocity}>
            Click
        </a.div>
    )
}

react-spring 魅力所在

最早吸引我入坑的 Demo,互動性的實現!


  • 完整為動態的使用場景而設計

    • useSpring:對個別物件操作動態樣式
    • useSprings:對列表型物件操作動態樣式,譬如像是上面對「一疊」塔羅牌做互動
    • useTrail: 以單一設定實現複數 stagger 性質的動態
    • useTransition:TransitionGroup 那般處理以動態形式處理物件的入場退場插拔效果
    • useChain:連續鏈式動畫(沒用過,不是很熟~)

  • 完整滿足需求的生態
    • react-use-gesture:mouse/touch gestures 事件資訊涵蓋超完整的操作 lib
    • react-three-fiber:藉由 ReactJSX 形式對 THREE.js 實現更好管理的狀態機&組件設計模式

  • 真正熱愛創作的大神在開疆闢土

Paul Henschel 是 react-spring 的開創者

像上面這個 react-spring 下的子專案將 3D 繪圖產出的 gltf 轉成 jsx 的形式放進 react-three-fiber 去做進階互動

v9 絕對是入坑最佳時機!

Alec Larson 是 v9 的主要維護者


v8 在功能上其實已經十分完整,v9 則是將整個 api 做了大重構,更增進了開發體驗。經過他接近一年努力,也完善更多使用情境的寫法支援。



兩個我最有感的:

傳入 animated 的時候不用再過一層 transform / interpolate

const { x, y } = useSpring({ x: 0, y: 0 })
// v8
import { to } from "react-spring"
const transform = to([x, y], (x, y) => `translate(${x}px, ${y}px)`)
return <a.div style={{ transform }} />
// v9
return <a.div style={{ x, y }} />

Improved async animations

// Chaining with an array
useSpring({
    to: [
        { opacity: 1, config: { duration: 1000 } },
        { color: "red", delay: 1000 },
    ],
    from: { opacity: 0, color: "black" },
})

// Chaining with an async function
useSpring({
    to: async (next) => {
        await next({ opacity: 1, config: { duration: 1000 } })
        await next({ color: "red", delay: 1000 })
    },
    from: { opacity: 0, color: "black" },
})

突然結語

其實這篇是算是綜合 逃避工作想對 MDX 完整測試慶祝期待已久v9 三個動機而寫成的哈哈...

react-springGSAP 都是用的會讓人感動的工具,有這兩樣特性互補的神器真的是想做什麼都的心應手啊~!


最後~ 歡迎幫我下面按個心得~ 讓我知道有人讀完這篇了~感恩XD

關於 react-spring 進一步詳情請參閱

react-spring v9 文檔

v9 Breaking Changes

v9 Changelog

React Podcast 56: Paul Henschel on React Spring

Gore Wang

Gore Wang - UXE 網站前端工程師

享受替願景及企圖編織傳達 🎨

熱愛為躍升性體驗創造工具 🚀

linkedinfacebookgithubemail