視覺解說器
使用者看到的是最終畫面。播放按鈕會重播一次動作。 因此,靜止畫面本身必須能說明一切;動畫僅用於顯示事物的順序。
這是內部風格:深色墨線、一個綠色強調色和沉穩的節奏,搭配白色背景、細線取代陰影。
可讀性優先
圖像是以 1:1 的比例繪製在約 15px 的終端機文字旁邊。任何小於此尺寸的文字,其可讀性都會比周圍的對話更差,使用者會無法閱讀。
- 無小於 16px 的文字(程式碼字型為 15px)。如果內容無法容納,請縮減內容或將視覺圖拆成兩張;切勿縮小字級。
- 字型:Atkinson Hyperlegible(專為易讀性設計),並使用下方備用字型。內文 400,標題 700。
- 每個圖片的元素要少:每行約 5 個節點,最多兩行。
代碼 (Tokens)
:root{
--bg:#fff; /* 永遠白色 */
--ink:#1d2520; /* 主要文字、主要線條 */
--muted:#5f6561; /* 次要文字、線上的標籤 */
--faint:#9aa19c; /* 非活躍節點、備用/可選路徑 */
--line:#e4e4e0; /* 卡片邊框、分隔線、網格 */
--tint:#f4f2ef; /* 罕見填色:程式碼區塊、高亮行 */
--accent:#3b7a57; /* 唯一的顏色:重要的路徑 */
--bad:#9a4b3c; /* 僅用於「不 / 受阻」標記,絕非裝飾 */
}
body{font:400 18px/1.4 "Atkinson Hyperlegible",Inter,system-ui,sans-serif;color:var(--ink)}
code{font:15px "Intel One Mono",ui-monospace,monospace}
- 字級比例:標題 26px/700、節點標籤 18px、子標籤和線標籤 16px。標題使用
letter-spacing:-.01em。數字使用font-variant-numeric:tabular-nums。 - 線條:邊框 1px。連接線 1.5px,
stroke-linecap:round。沒有任何線條粗於 2px。 - 圓角:卡片 10px、標籤/晶片 8px、藥丸型標記 999px。無其他數值。
- 間距:8px 網格。主體内邊距 24px。卡片内邊距 14px 18px。節點間距 96-120px,以便放置線標籤。
- 強調色的使用:一條路徑、一個邊框或一個標籤/晶片。如果有兩個東西是綠色的,其中一個就不該是綠色。
畫布 (Canvas)
- 寬度 = 內容 + 48px 内邊距。典型值 600-1100。永遠不要為了「填滿」而設定成全寬。內容從左上角開始。
- 行數:內容高度 + 48px,除以19,無條件進位。單行流程為 9-11 行;雙層為 16-20 行。最多 30 行。
- 寬而短優於高。優先採用從左到右的流向;在變高之前,先換行到第二列。
- 基礎樣式為白色、16px 内邊距、
box-sizing:border-box、overflow:hidden。請自行設定body{padding:24px}和背景色。
佈局 (Layout)
- 閱讀順序:標題在左上角,然後從左到右,再從上到下。動畫遵循相同順序。
- 標題是一個概括要點的簡單句子(「快取命中會跳過資料庫」),而不是一個主題(「快取」)。
- 卡片僅用於存在的東西(服務、表格、帳戶、步驟)。標籤、數值和註解為純文字。
- 連接線:在與節點相同的定位父層中使用 SVG 疊加層(
position:absolute;inset:0;overflow:visible)。使用getBoundingClientRect()測量端點;永遠不要硬編碼座標。在線條和卡片之間保留 8px 間距。 - 箭頭:一個開放的 V 形路徑
l5 4-5 4,顏色與線條相同。不使用實心三角形或<marker>預設值。 - 直線優先。曲線僅用於繞過某物:一個三次貝塞爾曲線,水平切線(
C midX y1, midX y2, x2 y2)。 - 未發生的路徑:
--faint顏色,虛線2 5。受阻的路徑:同上,並在其中點加上一個小的--badx 標記。 - 線標籤位於線條上方居中,使用
--muted顏色 16px,2-3 個字詞。 - 移動中的資料(請求、金額、訊息)是一個小的深色藥丸:
--ink背景、白色 16px 文字、半徑 999px。它最後會停在其目的地,因此靜止畫面會顯示它。 - 圖表:一個軸、2px 線條或細長條、網格線使用
--line、重要的數列使用--accent其餘使用--faint、直接使用標籤而非圖例、數值使用--ink顏色(永遠不要使用數列的顏色)。如果某個數字是重點,則顯示該數字,而非圖表。
動畫 (Motion)
- 每個場景都以有意義的最終幀結束。無淡出、無循環、無淡出效果。整個動畫至少要在
duration_ms之前 200ms 結束。 - 典型總時長 2-4 秒。保持在 6 秒以下,除非是多步驟的故事。
duration_ms= 最後結束時間 + 300ms。 - 順序:標題,然後按閱讀順序的節點,接著繪製連接線,然後標籤,接著移動的資料點,最後是結束狀態的變化。一個事物在另一個事物結束前開始(重疊約 0.1-0.3 秒);絕不可以同時發生,也不可以嚴格一個接一個。
- 數值:
| 項目 | 起始狀態 | 持續時間 | 緩動函數 (GSAP / cubic-bezier) |
|---|---|---|---|
| 標題 | opacity 0, y 8 | 0.6s | power3.out / .16,1,.3,1 |
| 卡片 | opacity 0, y 12, scale .97 | 0.8s, 交錯 0.12s | expo.out / .16,1,.3,1 |
| 連接線繪製 | dashoffset L+4 | 0.7s, 交錯 0.1-0.3s | power2.inOut / .65,0,.35,1 |
| 箭頭 | opacity 0 | 0.2s, 在線條結束時 | 線性 |
| 線標籤 | opacity 0, y 4 | 0.5s | power3.out |
| 資料點移動 | 沿著線條移動 | 1.0s | power2.inOut |
| 數值切換 | 舊值: y -8, blur 3px, 淡出 0.35s; 新值: y 8, blur 3px, 淡入 0.5s | power2.in / power3.out |
- 僅對
transform、opacity、filter: blur和 SVGstroke-dashoffset進行動畫處理。 - 永遠不要從
scale(0)開始;從 .97 開始。無彈跳、無過衝、無彈性效果。唯一的例外是為「不」標記使用一個小的back.out(1.6)彈出效果。 - 從不讓使用者在移動時閱讀的文字產生動畫;讓其定位後,再移動下一個項目。
- 適用時使用物理隱喻:如果東西是信件、發票或卡片,請像移動真實物體一樣移動它。
函式庫 (Libraries)
在撰寫任何 HTML 之前,根據圖片的類型挑選函式庫。手動實作函式庫已提供的功能(力導向佈局、數學公式、程式碼高亮)會看起來更糟且更容易出錯。
| 圖片類型 | 使用的函式庫 | 原因 |
|---|---|---|
| 卡片和箭頭、流程、一系列步驟 | HTML 卡片 + SVG 疊加層 + GSAP 時間軸 | 完全控制樣式;GSAP 安排步驟順序 |
| 簡短的入場動畫、列表出現 | Motion animate + stagger,或 CSS @keyframes | 比時間軸更少的程式碼 |
| 具有許多節點且無法手動定位的圖形 | elkjs (分層佈局) 或 d3-force,然後以我們的樣式繪製 | 佈局數學難以手動正確完成 |
| 真正的圖表(軸、多個數據點、比例尺) | d3(比例尺、軸、形狀)並手動設定樣式 | Chart.js 僅用於快速長條圖/折線圖;需要重設其預設樣式 |
| 數學、公式 | KaTeX | 真正的數學排版 |
| 程式碼 | highlight.js 搭配 github 主題,字型 15px | 在白色背景上可讀的顏色 |
| 大量粒子、模擬、物理效果 | <canvas> 搭配 rAF,由 performance.now() 驅動 | 對於數百個項目來說,DOM 太慢了 |
| 3D 場景 | three.js (ES module + importmap) | 僅在深度是重點時使用 |
| 「粗略想法 / 草稿」風格 | rough.js | 故意使用手繪風格的線條 |
解釋器永遠不要使用 Mermaid:其預設外觀與此風格衝突。請改用 elkjs 進行佈局。
已錨定 (Pinned),所有函式庫皆從 cdn.jsdelivr.net 載入(前綴 https://cdn.jsdelivr.net/npm/):
- GSAP:
[email protected]/dist/gsap.min.js(全域gsap)。不要使用paused:true;讓它在載入時播放。 - Motion:
[email protected]/dist/motion.js(全域Motion;const {animate, stagger} = Motion)。 - d3:
[email protected]/dist/d3.min.js(全域d3)。 - elkjs:
[email protected]/lib/elk.bundled.js(全域ELK;new ELK().layout(graph)回傳一個 Promise)。 - Chart.js:
[email protected]/dist/chart.umd.min.js(全域Chart)。設定animation:false或其自己的動畫時長在duration_ms之內。 - KaTeX:
[email protected]/dist/katex.min.js+[email protected]/dist/katex.min.css。 - highlight.js:
@highlightjs/[email protected]/highlight.min.js+.../styles/github.min.css。 - rough.js:
[email protected]/bundled/rough.js(全域rough)。 - three.js:
[email protected]/build/three.module.min.js透過 `<script type=