iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0
自我挑戰組

向網頁施點魔法粉 framer-motion 系列 第 13

#13 The New Style - useMotionValue & useMotionTemplate

  • 分享至 

  • xImage
  •  

motion 元件都會使用到 Motion value,主要用來訂閱 (subscribe) 動畫或運動狀態的值,一般來說 motion 元件自動會建立這些值,另外也提供一些 Hooks 可以更細微控制。

目錄

  1. 控制 style : useMotionValue
  2. 使用 useMotionValue
  3. 另一種選擇 : useMotionTemplate

useMotionValue

useMotionValue 的使用很簡單 :

import { motion, useMotionValue } from "framer-motion"

export function MyComponent() {
  const x = useMotionValue(0) // 給予初始值
  
  // 應用在 style 上,這邊是 物件 key 跟值同名的省略 { x : x }
  return <motion.div style={{ x }} />
}

透過手動建立 MotionValue,可以結合其他的事 :

  1. 更新或讀取值
  2. 傳給多個的元件,跨元件同步動畫數值
  3. 透過 useTransform Hooks 串聯多個 motion value。
  4. 不觸發 React re-render 的情況下更新值
  5. 訂閱更新

以第 4 點來看,我就會想到 useRef ,同樣更新也不會觸發 re-render,看一下 原始碼 確實是以 useRef 來實作 :

// 內部使用的 useConstant
export function useConstant<T>(init: Init<T>) {
    const ref = useRef<T | null>(null)
    if (ref.current === null) {
        ref.current = init()
    }
    return ref.current
}

在開發環境下的嚴格模式 (strict mode) React 在初始化包含 useMemo 會執行兩次 render ,但 ref 不會,可以避免這樣的情形發生。

一般我們寫 inline style,style 是物件,避免每次 render 重新產生,要嘛提到 function Component 外用一般物件建立,要麼在 function 內部將物件給記住,但 useMemo
也會被渲染機制影響,所以選擇 ref ,另外也要當下的最新值,同步畫面。

motion value 可以傳給其他的元件,當值更新時,所有用到該值的元件都會一併更新。

使用 useMotionValue

  • set() : 更新值,如上面所說的是使用 ref 為基底,所以並不會觸發 React re-render
  • get() : 讀取值,motion value 可以是 字串或是數字
  • getVelocity() : 回傳 數字類型 的速度值,如果不是數字,回傳 0 。
  • isAnimating() : 是不是正在播放動畫中
  • stop() : 暫停目前正在執行的動畫
  • onChange((lastest)=> {}) : 訂閱目前動畫的數值。通常在 useEffect 使用
  • destroy() : 取消訂閱 (clean up)
const x = useMotionValue(0)

x.set(10)
x.get()

// 訂閱目前的值
useEffect(() => {
  x.onChange(latest => {})
}, [])


// 使用在一般 html 標籤的 style 上
<motion.div style={{ x }} /> 

// 使用在 SVG 
<motion.circle cx={x} />
  • 用 opacity 從 0 到 1 來看內容 :
const opacity = useMotionValue(0);

useEffect(() => {
    opacity.onChange((latest) => {
        console.log(latest);
    });
    console.log(opacity);
}, []);

 <motion.div
  ref={boxRef}
  style={{
      width: 100,
      height: 100,
      border: "1px solid black",
      opacity: opacity,
  }}
  animate={{
      opacity: 1,
  }}
>


印出 opacity 的內容物,可以看到除了最新值,也可以透過 getPrevious() 方法拿到物件裡面的 prev。

  • onChange 訂閱事件中可以看到每次動畫按照 16.6 ms 更新時的 opacity 的值

useMotionTemplate

useMotionTemplate 一樣也可以建立 motion value ,差別在使用樣板字面值 ( Template literals ) 表示,回傳值就是最新的運動狀態值,並且也是樣板字面值。只要 template 一變動,就會更新成最新的值。

  • 也是用兩個小點
useMotionTemplate``
  • 搭配 useMotionValue
import { useMotionTemplate } from "framer-motion" // 引入

const x = useMotionValue(100)

// 等同於 transform.get() === transform(100px)
const transform = useMotionTemplate`transform(${x}px)`

// 直接插入,不用 `x : x` 這樣
<motion.div style={{ transform }} />

參考資料

  1. 官方文件 : Motion values overview | Framer for Developers

上一篇
#12 Point and Line to Plane - SVG Animation
下一篇
#14 Move & Bounce Again - useTranfrom & useSpring
系列文
向網頁施點魔法粉 framer-motion 15
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言