Communitygithub.com

anhlamtruong/living-ui-effects

添加即時、互動的網頁特效,讓頁面載入後持續保持動感。包含 WebGL 極光/液態光效、全息投影 3D 傾斜卡片、跟隨游標的網格聚光燈、RGB 分離故障標題,以及連結與按鈕的懸停文字解碼效果。

living-ui-effects 是什麼?

這個 AI 代理技能(anhlamtruong/living-ui-effects)提供一組即插即用的 React/Next.js 元件,為網頁注入四種高品質、可存取的動態效果:極光英雄區(Aurora hero)、全息投影卡片(Hologram cards)、網格聚光燈(Grid spotlight)以及故障與解碼文字(Glitch and scramble type)。其核心設計哲學是「活潑但絕不干擾」:所有效果皆共享一個 requestAnimationFrame 迴圈(ticker.ts),閒置時自動休眠以節省資源;動畫僅作用於 transform、opacity 與 clip-path,確保零版面偏移;並在觸控裝置上自動降級為更適合的體驗。技能支援多個平台(React 18/19、Next.js),並透過 CSS 自訂屬性和 React props 提供豐富的佈景主題選項。詳細的 SKILL.md 文件說明瞭安裝方式、八條效能與可存取性規則、每個效果的運作原理,以及嚴格的圖層與 motion 偏好設定,幫助開發者在不犧牲網頁效能或可用性的前提下,打造令人驚嘆的互動體驗。

相容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/anhlamtruong/living-ui-skills/tree/HEAD/skills/living-ui-effects

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

動態效果

四個讓頁面內容顯示後持續保持動感的特效:

  • 極光英雄區: 一個 WebGL 的柔和光場,位於英雄區後方,並會隨游標彎曲。
  • 全息投影卡片: 卡片會朝游標方向傾斜、微微上提,並產生眩光與虹彩邊緣。
  • 網格聚光燈: 頁面背景網格會在游標周圍形成一個柔和的亮圈。
  • 故障與解碼文字: 標題在出現時會產生 RGB 分離效果,連結標籤則會在懸停時解碼。

標準是「生動,但絕不礙事」。使用者應感受到頁面對他們的操作有所回應,但任何效果都不能造成卡頓、版面位移、阻擋點擊,或對螢幕閱讀器隱藏文字。

即時範例:https://www.lamanhtruong.com(將滑鼠懸停在專案卡片上、在英雄區移動、懸停導航列)。

assets/fx/ 內容

檔案角色
ticker.ts一個共用的 requestAnimationFrame 迴圈,在無人要求影格時進入休眠
built.ts就緒閘門(isBuilt, buildActive)、frozen()、種子化 PRNG、指標媒體查詢
aurora.tsWebGL 著色器、其品質縮放器與生命週期(createAurora)
hero-shader.tsx<HeroShader />,極光效果的 React 包裝器
holo.ts卡片傾斜 + 眩光 + 光澤效果、彈簧物理、注入覆蓋層
spotlight.ts跟隨游標的網格聚光燈(僅 transform)
glitch.ts, glitch-copies.tsx故障排程器、<GlitchCopies /> 與 <GlitchHeading />
scramble.ts, scramble-text.tsx解碼執行器與 <ScrambleText /> 標記鉤子
fx-root.tsx<FxRoot />:一次掛載全息投影、聚光燈、故障與解碼效果
fx.css所有樣式,使用 --fx-* 自訂屬性設定主題

此程式碼僅需 React,無需動畫函式庫。這些模板在嚴格的 TypeScript 下可通過 React 18 與 19 類型的型別檢查。

安裝

  1. 將 assets/fx/ 複製到應用程式中,例如 src/components/fx/。請將檔案放在一起,因為它們會透過相對路徑互相引用。
  2. 掛載它。頁面本身可以保持為靜態伺服器元件;只有 FxRoot 和 HeroShader 是用戶端元件。
import { FxRoot } from "@/components/fx/fx-root";
import { HeroShader } from "@/components/fx/hero-shader";
import { GlitchHeading } from "@/components/fx/glitch-copies";
import { ScrambleText } from "@/components/fx/scramble-text";

export default function Page() {
  return (
    <div className="fx-grid">                        {/* 固定的基礎網格;若已有自己的網格則可跳過 */}
      <main className="relative z-[1]">              {/* 內容位於網格與聚光燈之上 */}
        <section className="relative min-h-[80vh]">  {/* 英雄區必須是 position: relative */}
          <HeroShader />                             {/* 第一個子元素,位於英雄區內容後方 */}
          <h1>…</h1>
        </section>

        <GlitchHeading as="h2" className="text-5xl font-semibold">精選作品</GlitchHeading>

        <article data-holo className="rounded-2xl border p-6">
          <a href="/work/x" className="inline-flex items-center gap-2">
            <ScrambleText>閱讀案例研究</ScrambleText> <ArrowIcon />
          </a>
        </article>
      </main>
      <FxRoot />                                     {/* 每頁一次,位於網格包裝器內部 */}
    </div>
  );
}
  1. 設定主題:編輯 fx.css 頂部的 --fx-* 變數,並將 palette / intensity 傳遞給 <HeroShader />(請參閱下方「設定主題」章節)。
  2. 在完成前,使用 references/verification.md 中的 Playwright 檢查進行驗證。

透過 props 關閉特定效果:<FxRoot holo={false} spotlight={false} />。將傾斜效果指向現有卡片,無需修改其標記:<FxRoot holo={{ selector: ".card", within: "main" }} />。

保持流暢的規則

這些規則區分了「高級感」與「卡頓感」。每一條規則的存在,都是因為違反它曾導致明顯的問題。

  1. 等待內容準備就緒。 永遠不要在另一個腳本仍在揭示或測量元素時,對其施加傾斜、故障或解碼效果。在元素揭示過程中對其施加 transform 會破壞正在為其製作動畫的腳本的測量結果,導致看起來像卡頓。built.ts 實現了一個約定:當 html[data-build="on"] 被設定時,一個元素僅在它的 [data-build-item] 和 [data-build-section] 帶有 [data-built] 屬性質時才算就緒。如果頁面上沒有建置引擎,所有元素都會立即被視為已建置。任何入場動畫都可以透過設定這些屬性來選擇加入。詳細資訊請參閱 references/gating-and-performance.md。

  2. 使用單一影格迴圈,並讓它休眠。 每個效果都訂閱 ticker.ts,而不是自行呼叫 requestAnimationFrame。當訂閱者回傳 false 表示已穩定時,且無人需要影格時,迴圈就會停止。這樣一來,閒置頁面就不會消耗任何資源,且效果之間不會爭搶影格。故障效果完全不使用影格:它是由屬性切換的 CSS keyframes。

  3. 僅對 transform、opacity 和 clip-path 進行動畫。 這些屬性會停留在合成器層。這裡沒有任何效果會改變大小、在流程中的位置或字型度量,因此從根本上保證了零版面位移。解碼效果將真實文字保持在原位(透明),並在覆蓋層中繪製字形,因此即使標籤在變化,其寬度也永遠不會改變。

  4. 根據輸入條件匹配效果。 傾斜效果和跟隨式聚光燈僅在 (hover: hover) and (pointer: fine) 條件下存在。在觸控裝置上,手指下的傾斜效果會被視為錯誤,因此觸控裝置會在觸碰點獲得一個短暫的淡入效果,以及一個自漂移的著色器鏡頭。

  5. 在靜止時不要碰觸 React 的 DOM。 全息投影覆蓋層 <span> 和行內 transform 是在卡片啟動時添加,並在卡片恢復靜止時移除。在靜止時,DOM 完全符合 React 的渲染結果,因此重新渲染、hydration 以及卡片自身的 :hover 樣式都能正常運作。故障效果層與解碼效果的 <span> 是直接在標記中撰寫(<GlitchCopies>、<ScrambleText>),而不是在執行時從外部插入到 React 擁有的節點周圍;從外部重組 React 的樹結構會破壞協調機制。

  6. 保持文字的真實性。 故障效果複製層僅限於用戶端且帶有 aria-hidden 屬性,因此靜態 HTML、頁面內查找功能以及螢幕閱讀器只會看到每個標題一次。解碼效果覆蓋層帶有 aria-hidden 屬性,且真實標籤在整個過程中都是可存取名稱。

  7. 進行清理,並在 StrictMode 下正常運作。 每個 setup* 都會回傳一個 dispose 函式,用於移除監聽器、觀察器、覆蓋層和行內樣式。著色器會釋放其緩衝區和程式,但刻意不呼叫 WEBGL_lose_context,因為 React StrictMode 會在同一個 canvas 上進行掛載、卸載和重新掛載,而遺失的 context 將永遠保持遺失狀態。

  8. 為 GPU 做預算規劃。 著色器在像素預算下以降低的解析度渲染,僅在可見時繪製,在高更新率螢幕上將繪製次數限制在每秒約 80 次,並在頁面執行速度低於約 38 fps 時自動降低一檔品質。它在使用軟體備援方案時(failIfMajorPerformanceCaveat)拒絕啟動,寧可什麼都不渲染,也不渲染緩慢的內容。

效果

極光英雄區著色器(aurora.ts、hero-shader.tsx)

著色器是經域扭曲的數值雜訊(由 fbm 扭曲的 fbm),繪製成寬廣的輝光加上沿著扭曲輪廓的細亮條紋。指標充當透鏡:它會放大並旋轉附近的流體。進入英雄區、點擊或快速移動時會產生一個柔和漣漪環。輸出為預乘 alpha,並在每個邊緣淡出,以便頁面背景和任何固定網格能夠透出,且英雄區能與下一個區塊融合,而不是以一個盒子形狀結束。

生命週期:在其區塊建置完成後,canvas 會在 1.8 秒內淡入。它僅在區塊位於螢幕上(使用 IntersectionObserver,邊距 80 px)且分頁可見時才進行渲染。如果缺少 WebGL,則不渲染任何內容。

調節參數:

  • palette:{ veil, primary, secondary, accent } 十六進位色碼。調色板會被編譯到著色器中,因此變更需要重新掛載。
  • intensity:乘以光線強度。0.6 為平靜,1.4 為強烈。
  • 文字可讀性:著色器會將光線偏向遠離左下角的位置,英雄區文字通常位於此處。如果您的文字在其他位置,請更改 bias 行(請參閱參考文件)。

在更改著色器數學運算、速度或品質縮放器之前,請閱讀 references/aurora-shader.md。

全息投影卡片(holo.ts)

委派的 pointerover 事件會找到精細指標下的卡片。啟動時,它會測量卡片未經轉換的邊界框,注入一個帶有 aria-hidden 屬性的覆蓋層 <span>,並執行一個臨界阻尼彈簧(以 120 Hz 進行子步進,使其在 60 或 144 fps 下表現一致),朝向指向指標的傾斜角度。游標下方的邊緣會朝向觀看者靠近。離開時,它會以輕微的 overshoot 彈回,並在穩定後移除所有內容。

  • 傾斜角度會根據卡片大小縮放,範圍為 2.5° 至 7.5°:大卡片傾斜角度較小,以免其遠端擺動。透視距離為較長邊的 1.6 倍。上提量為 3 px。
  • 覆蓋層在指標位置具有徑向眩光、微弱的對角線漸層色調,以及一個遮罩到邊框的圓錐漸層環(周圍暗色,指標附近明亮)。
  • 在 overflow: hidden 的卡片上,1 px 的邊框盒會被裁切,因此環會被繪製在內部(.fx-holo--in,自動偵測)。
  • 覆蓋層位於 z-index: 3,並設定 pointer-events: none,因此它會在卡片內部的控制項(例如影片切換開關、連結)上方繪製,而不會阻擋點擊。
  • 一個 position: static 的卡片僅在啟動時才會獲得 position: relative 屬性。
  • 選擇退出:在卡片或其任何祖先元素上設定 data-holo="off"。在 html[data-fx-freeze] 存在時暫停所有傾斜效果,以便讀取真實的矩形資訊(例如檢查器、螢幕截圖階段)。living-ui-swarm 檢查切換功能會自動偵測。

網格聚光燈(spotlight.ts、div.fx-spot)

一個 560 px 的固定視窗,帶有徑向遮罩,包含一個較亮的基礎網格副本。每個影格僅移動兩個 transform:視窗跟隨游標,並且內部的網格會向後移動 floor(x / cell) * cell - x,使其線條精確對齊基礎網格的格點。兩個網格都必須錨定在視口原點,而 .fx-grid::before 正是如此。當游標停止時,追蹤 lerp 會穩定下來,迴圈進入休眠。當視窗失去焦點或指標離開視窗時,它會隱藏。觸控裝置會在觸碰點獲得一個 360 ms 的淡入效果。

網格單元格、細線間距和視窗大小來自 --fx-grid-cell、--fx-grid-fine 和 --fx-spot-size(px 值),因此 CSS 保持為唯一的真實來源。

故障標題(glitch.ts、<GlitchHeading> / <GlitchCopies>)

標記:一個包含真實標題(.fx-glitch__base)和兩個僅限用戶端副本的 .fx-glitch[data-fx-glitch] 包裝器。GlitchHeading 會撰寫所有這些內容;當標題已存在且帶有其他屬性時,請直接使用 GlitchCopies。

  • 入場效果: 當一個已建置的標題至少有 50% 進入視野時,持續 560 ms。相同的標題在 2.5 秒內不會重複播放。
  • 微型故障: 每 8-14 秒對一個隨機的可見標題觸發一次,持續 260 ms。
  • 隨機性是種子化的,因此每次訪問和在測試中的節奏都是相同的。
  • 一次只有一個標題會產生故障效果;入場效果會彼此排隊。
  • 透過由 [data-glitch] 切換的純 CSS keyframes 實現:使用 clip-path: inset() 切片、使用 translate3d 偏移,並使用 text-shadow 分割顏色。
  • --fx-gin 會根據每次執行的情況縮放偏移量。

解碼文字(scramble.ts、<ScrambleText>)

僅包圍標籤,而非圖示:<a><Icon/> <ScrambleText>{label}</ScrambleText></a>。

  • 觸發條件: 對包含 a、button 或 [data-fx-scr-host] 的元素進行懸停(非觸控)或鍵盤 :focus-visible。
  • 持續時間: 360-520 ms,取決於文字長度。
  • 解析過程: 字元從左到右解析,帶有些微抖動。未解析的尾部以約 18 Hz 而非每個影格重新繪製,使其看起來像解碼過程,而非雜訊。
  • 字形池: 區分大小寫,且 @ . / : - 等標點符號保持不變,因此電子郵件和路徑在解碼過程中仍可閱讀。
  • 清理: 覆蓋層始終會被清空,包括為背景分頁(此處 rAF 會停止)提供的計時器備援方案。

對純行內連結使用 inline(保留底線)。對填滿的按鈕使用 quiet,同時在按鈕上設定 --fx-scr-tail: currentColor。

設定主題

變數使用者預設值
--fx-accent故障效果層 A、解碼字形、聚光燈輝光、全息投影陰影#5eead4
--fx-hot故障效果層 B#f472b6
--fx-sheen-1..5全息投影環,依序圍繞圓圈青綠色、藍色、紫色、粉紅色、琥珀色
--fx-grid-line, --fx-grid-fine-line基礎網格線低透明度石板色
--fx-grid-cell, --fx-grid-fine網格間距(px)96px, 24px
--fx-spot-size聚光燈視窗(px)560px
--fx-mono解碼字形字型系統等寬字型
--fx-scr-tail每個元素的字形顏色覆蓋未設定

顏色透過 color-mix(in srgb, …) 混合,所有現代瀏覽器均支援。著色器使用自己的調色板:<HeroShader palette={{ accent: "#22d3ee", secondary: "#f0abfc" }} intensity={0.8} />。

從與頁面強調色相同的色系中選擇著色器顏色。著色器是一種氛圍,而非第二個品牌顏色;如果初次訪問的使用者在看到標題之前就注意到著色器,那就表示它太過強烈了。

圖層

圖層z-index備註
基礎網格(.fx-grid::before,固定)0錨定在視口原點
聚光燈(div.fx-spot,固定)0在樹狀順序中位於網格之後,因此它繪製在網格之上
內容(main,footer)1必須使用 position: relative; z-index: 1 定位
英雄區著色器(canvas.fx-shader)auto在英雄區內部絕對定位且為第一個子元素,以便後續內容繪製在其之上
全息投影覆蓋層3(在其卡片內部)pointer-events: none
固定頁首、任何建置動畫覆蓋層 canvas高於內容不受影響

如果聚光燈顯示在您的文字之上,表示內容包裝器缺少 position: relative; z-index: 1。

動畫偏好設定

在 lamanhtruong.com 上,站長選擇為所有訪客(包括設定 prefers-reduced-motion 的訪客)播放所有效果,因為在其作品集網站上,動畫本身就是重點。這是一個合法的產品決策,但請有意識地做出這個決定。若要尊重該設定:

  • <FxRoot motion="reduced" /> 會關閉所有 assets/fx/ 的效果。這是最簡單的選擇。
  • 或者,使用 media prop:<FxRoot media={{ prefersReducedMotion: "reduce" }} /> 僅在 prefers-reduced-motion: reduce 時關閉效果,而 "no-preference" 則會關閉所有效果。
  • 對於受控的體驗,請使用 motion="controlled" 並自行管理 html[data-fx-paused] 屬性。

請勿在 prefers-reduced-motion: reduce 時強制開啟效果,除非您已確認使用者是為了體驗效果而特意造訪。

驗證

在 references/verification.md 中為每個效果提供了 Playwright 檢查。在完成實施之前執行它們,以避免將來出現回歸問題。

配備觸控板的 MacBook 可能會同時觸發懸停和觸控設備路徑,這是混淆的常見來源。references/input-ambiguity.md 解釋了邊緣情況以及 FxRoot 如何處理它們。

參考文件

references/ 中的文件擴展了此處的摘要:

文件涵蓋內容
gating-and-performance.md建置閘門的工作原理、為何 built.ts 檢查 data-build、著色器品質縮放器決策樹
aurora-shader.md著色器數學原理(雜訊、域扭曲、合成)以及調整速度或筆觸寬度的起點
input-ambiguity.md在混合輸入(觸控板 + 滑鼠 + 觸控)下,設備類型的 holo.ts 和 spotlight.ts 的模糊性;代理程式應遵循的決策樹
verification.mdPlaywright 測試:每個效果的通過條件、元素狀態、數值容差、可視性檢查

閱讀它們以了解邊界情況下的行爲;在更改效果邏輯之前請先閱讀它們。

與代理程式的協定

  • 除非要求使用 spotlight.{{direction}} 的變體,否則絕不更改 assets/fx/ 中的檔案。這些效果是經過硬體調整的,引入邊界情況會削弱它們。
  • 當要求「極光」、「液體」(或其他動態背景)、「3D/傾斜/全息投影卡片」、「跟隨游標/聚光燈」、「故障/解碼文字」或「懸停效果」時,無論使用者如何描述,都使用此技能。當使用者要求「活潑」、「高級感」、「驚豔」或「哇」的時候,也使用它。使用者也許不了解這些術語;請將其翻譯到這個技能中。
  • 第一步始終是將 assets/fx/ 複製到適當的位置(src/components/fx/ 或類似位置)。在整合之前,檢查 Next.js 配置以確保 canvas 不會被伺服器端渲染。
  • 完成複製後,提供一個簡短的整合計劃,然後編寫程式碼。始終使用 <FxRoot />、<HeroShader />、<GlitchHeading /> 和 <ScrambleText /> 的方式,與安裝步驟中的範例相同。
  • 如果頁面缺少網格,請說明如何添加網格,或建議 className="fx-grid" 包裝器。
  • 在主題化顏色時,從一個突出的強調色(通常來自按鈕、連結或徽章)開始,並使用工具將其轉換為 --fx-accent。提取一個次要顏色(顏色較深、飽和度較低的版本)作為 --fx-hot。如果品牌顏色是中性色,請選擇一種鮮豔的青色/紫色/粉色/琥珀色組合來使用。
  • 當使用 color-mix() 時,如果背景顏色與預期不同,解釋如何進行調整。
  • 在標記 data-holo 卡片和設定它們的 CSS 時,提供簡短的程式碼範例。
  • 如果該頁面有或將要有 next/font,請確認 <FxRoot /> 位於字型載入器內部,或者在初始渲染後重新掛載整個內容,以便字型度量在傾斜效果測量之前是最終的。
  • 在 verification.md 上執行 Playwright 檢查,並在整合完成時預覽頁面。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相關技能