文字動畫
液態折射揭露
以 SVG 位移濾鏡讓內容像隔著液滴折射晃動,點擊或進入視口時折射歸零、液面破裂,清楚揭露標誌或標題。
把標誌、標題或任何內容包進一顆緩慢變形的液珠裡:內容隔著液面折射晃動,紋理頻率持續「呼吸」;觸發時位移先衝到極限、再以彈簧歸零,液珠外形攤平並向外噴出一圈小液滴,內容清楚現身。適合品牌開場、新版發佈標題與 hero 區塊的揭露瞬間。折射用的是內嵌的 SVG 濾鏡(feTurbulence+feDisplacementMap),不需要 canvas 或 WebGL。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add "https://webberui.com/r/liquid-reveal.json?t=<安裝 token>"Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
40
0.012
8
編輯中的呼叫預設屬性
<LiquidReveal/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add "https://webberui.com/r/liquid-reveal.json?t=<安裝 token>"或在 components.json 設定 registries 後,改用 @webberui/liquid-reveal 安裝。
使用
import * as React from "react";
import {
LiquidReveal,
type LiquidRevealHandle,
} from "@/components/ui/liquid-reveal";
// 點擊揭露:容器本身是按鈕
<LiquidReveal trigger="click" contentLabel="水禾 品牌字標" tint="rgba(56,189,248,0.16)">
<Logo />
</LiquidReveal>;
// 進入視口自動揭露,並以 ref 重播
function Headline() {
const ref = React.useRef<LiquidRevealHandle>(null);
return (
<>
<LiquidReveal ref={ref} trigger="inView" droplets={10} grain>
<h2 className="text-4xl font-bold">新版上線</h2>
</LiquidReveal>
<button type="button" onClick={() => ref.current?.reset()}>
重播
</button>
</>
);
}Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 被液滴包住、最後要揭露的內容(文字、SVG 標誌、圖示組合皆可) |
revealed | boolean | — | 受控的揭露狀態;未提供時由元件內部管理 |
defaultRevealed | boolean | false | 非受控模式的初始揭露狀態 |
onRevealedChange | (revealed: boolean) => void | — | 揭露狀態改變時回呼 |
trigger | "click" | "inView" | "manual" | "inView" | click 讓容器成為按鈕;inView 有 60% 進入視口時自動破裂;manual 只靠 revealed 或 ref |
restScale | number | 40 | 待機時的位移強度(feDisplacementMap 的 scale);越大折射越扭曲 |
frequency | number | 0.012 | 折射紋理的基準頻率(feTurbulence 的 baseFrequency);越大波紋越細碎 |
droplets | number | 8 | 破裂時向外噴出的小液滴數量(0 為不噴) |
tint | string | — | 液滴染色(任何 CSS 顏色);寫入 --wb-liquid-reveal-tint,未設定時透明 |
grain | boolean | false | 在液面疊一層細噪點,增添擬物質感 |
revealLabel | string | "揭露內容" | trigger="click" 時,揭露前按鈕的無障礙名稱 |
contentLabel | string | — | 揭露後的無障礙名稱(內容描述);未提供時由 children 的文字命名 |
ref | React.Ref<LiquidRevealHandle> | — | 取得 reveal()/reset() 方法 |
className | string | — | 透傳到最外層容器 |
LiquidRevealHandle
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
reveal | () => void | — | 立即揭露(等同觸發一次破裂) |
reset | () => void | — | 回到液滴態,重新開始折射晃動 |
另外具名匯出 LiquidRevealTrigger、LiquidRevealHandle 與 LiquidRevealProps 型別。
CSS 變數
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
--wb-liquid-reveal-duration | <time> | 700ms | 揭露時液珠外形攤平、染色與高光淡出的時間 |
--wb-liquid-reveal-tint | <color> | transparent | 液滴底色;tint prop 會寫入這個變數 |
--wb-liquid-reveal-wobble | <time> | 6s | 液珠外形變形一輪的週期 |
細節
- 折射:容器內放一個 0×0 的內嵌 SVG,定義 feTurbulence(fractalNoise、2 個 octave、固定 seed)接 feDisplacementMap;children 所在的層以
filter: url(#id)套用,id 由useId產生,同頁多個實例互不干擾 - 不逐幀重繪:位移強度與紋理頻率都是 motion value,動畫中直接把數值寫進 SVG 屬性,不觸發 React 重新渲染
- 待機呼吸:
baseFrequency在frequency的 10/12 到 16/12 之間以 4 秒來回;元件離開視口就停止,省下濾鏡重算 - 破裂三段:位移先在 120ms 內衝到
restScale的 1.6 倍,再以彈簧(stiffness 180、damping 18)降到 0;同時液珠放大到 1.08 再回彈、外形攤平,並以確定性角度噴出小液滴(飛 24–60px、500ms 淡出)。動畫結束後濾鏡整個移除,內容回到原生清晰度 - inView 只在「進入」時觸發:呼叫
reset()時若仍在視口內,不會立刻又破裂,要捲出再捲回才會重播 - Safari 降級:Safari 對 HTML 元素套 SVG 位移濾鏡的支援不穩,偵測到時改用 CSS
blur(6px)漸變到 0,節奏相同
可及性
- 濾鏡純屬視覺:children 始終留在 DOM 中,文字可被選取、搜尋與朗讀
trigger="click"時容器是type="button"的按鈕,可用 Tab 聚焦、Enter/空白鍵觸發,並有focus-visible外框;揭露前的無障礙名稱為revealLabel(預設「揭露內容」),揭露後改為contentLabel,未提供時由 children 的文字命名- SVG 濾鏡定義、高光、噪點與噴出的小液滴都標記
aria-hidden - 使用者系統開啟「減少動態效果」時,直接顯示 children:不套濾鏡、不變形、不噴液滴,也沒有呼吸動畫