iso:動畫等角解說圖
一個場景在 6 到 14 秒的循環中講述一個產品故事:物件到來、光在它們之間傳遞、一次只亮起一個部分,然後循環回到第一個畫面。一個流程只有一種形狀。從節拍表中選擇故事需要的形狀:一個打開顯示其層次的盒子、兩個物件之間的交換、一個逐漸填滿的網格、一個旋轉選擇的堆疊,或透過懸停來抬起的拼貼。一切都是真實的 SVG 和 HTML 文字,隨其容器縮放,在螢幕外暫停,並在減少動畫模式下靜止。
此工具套件與此檔案位於同一目錄:
lib/iso.ts:Scene建構器(投影、方塊、面、線路、脈衝、標籤、動作軌跡)、scene.exportSvg()、responsive()和lazyBox()。lib/runtime.ts:使用 WAAPI 播放渲染後的場景,一旦場景的 40%(或半個螢幕高度,對於較高的場景)進入視野就開始其介紹,在螢幕外暫停,當它離開超過半個螢幕距離時移除其動畫,並在它返回前在同一畫格恢復,為減少動畫模式靜止,暴露window.isoSeek(t)。scripts/render.ts:將場景模組(預設導出Page)渲染為一個獨立的 HTML 檔案,並內嵌執行時期。scripts/shots.mjs:在選定的時間和寬度下對任何頁面截圖,並寫入sheet.png。scripts/check.ts:對磁碟上任一檔案進行嚴格的場景類型檢查(bun scripts/check.ts scene.ts)。scripts/page-idle.mjs:讓一個包含多個場景的頁面完整滾動一次,然後在 0%、20%、35%、50%、60% 和 80% 的位置停止,並在 CPU 為 1x 和 4x 時印出主執行緒時間(node scripts/page-idle.mjs page=<url> width=393 dpr=3 scroll=4)。在包含超過幾個場景的頁面上執行它。scripts/perf.mjs:在手機寬度下於 Chrome 中播放一個頁面,並對 CPU 進行節流(node scripts/perf.mjs page=<out.html> width=393 throttle=4 seconds=20),然後印出幀率、幀時間百分位數、延遲幀、長時間任務和主執行緒時間。在包含 30 個或更多移動物件的場景上執行它。scripts/contrast.mjs、scripts/render-all.ts:在靜止畫格和另外四個時刻,測量渲染頁面中每個文字與其字形正後方像素的對比度,如果低於 4.5:1 則失敗(node scripts/contrast.mjs <out.html>;bun scripts/render-all.ts <dir>先寫入所有目錄範例)。在每個場景完成前對其執行此檢查。scripts/study-rive.mjs、scripts/compare-rive.mjs:可選工具,用於逐幀研究你自己的.riv動畫,並與工具套件場景進行比較。兩者都需要runtime=<folder with canvas_advanced.mjs and rive.wasm>。examples/:64 個針對虛構產品的場景,每個都是一個導出example(標題、故事、提示、難度等級、類別、佈局、互動,以及art:來自art(compact, wide, breakpoint)的緊湊和寬場景)和預設導出Page的檔案,因此scripts/render.ts可以渲染其中任何一個。spot場景是單個小場景,art(compact)。hero.ts導出hero(一個Art),material-gallery.ts導出galleries(每個材質系列一個Art),每個都有預設導出Page。examples/shared.ts包含表面、顏色漸層、paints、字體和fitX(),後者用於將標籤保持在場景內最窄的卡片中。
選擇最接近你故事的範例,並在繪製前閱讀:
| 範例 | 難度 | 展示內容 |
|---|---|---|
notifications.ts | 簡單 | 最小化檔案:一個核心扇形展開到三個拼貼、脈衝、到達閃光與跳躍。請先閱讀此範例 |
ci-deploy.ts | 簡單 | 一個套件沿著鏈條跳躍、落地壓縮、僅在結尾的狀態顏色 |
file-upload.ts | 簡單 | 填充進度條的塊,然後複製擴散到邊緣點;空槽位的行進虛線 |
db-branching.ts | 簡單 | 一個虛線幽靈變成實線,一個變更拼貼滑回並合併 |
service-map.ts | 簡單 | 懸停:九個帶有 slab({ hover }) 的拼貼,懸停或點擊時顯示標籤,加上一個仍然講述故事的循環 |
auth-flow.ts | 中等 | 一個直立的 panel() 手機、輸入文字、攜帶令牌的交接幽靈 |
etl-pipeline.ts | 中等 | 來源拼貼沿著線路饋送到核心,線路隨之繪製,然後是一個填充與清空的網格;與它們所在的方塊分組的疊加層,使它們一起移動 |
queue-workers.ts | 中等 | 一個由事件表驅動的槽佇列,建置過程會驗證事件表(無溢出、無從空佇列提取) |
search-index.ts | 中等 | 淺色頁面上的線條藝術拼貼、輸入查詢、符合條件的點亮、排名結果上升 |
feature-flags.ts | 中等 | 遊玩:標籤中的真實 <button> 和一個內聯腳本,使用 seek 重播一個循環步驟 |
agent-tools.ts | 複雜 | 一個帶有衛星的核心、請求和回覆脈衝、一個逐字輸入的答案卡片 |
checkout-payment.ts | 複雜 | 一個帶有虛線返回路徑的步驟環,以及一個交叉淡化的狀態行 |
edge-cache.ts | 複雜 | 同一個請求兩次:到來源的冷未命中,然後在邊緣的熱命中 |
observability.ts | 複雜 | 三種訊號外觀(虛線、點、跨度)、帶有 heightTo 的 prism 條、警報與恢復 |
keycard-slot.ts、battery-charge.ts、tile-loader.ts、empty-box.ts | 點綴 | 一個小型物件,用於功能列表、載入器或空狀態;一個在插槽邊緣裁剪的直立 panel() 卡片、heightTo 單元格、對角線下降波、行進虛線 |
sync-loop.ts、inbox-fill.ts、alert-stack.ts、table-scan.ts | 點綴 | 壓扁到平面上的箭頭、一個玻璃前托盤、滑到一個堆疊上的卡片,帶有計數並騎在頂卡上的徽章、一個逐行滑動的透鏡 |
audit-ledger.ts、dns-resolve.ts、cron-jobs.ts | 簡單 | 帶有印章的頁面堆疊、一次一跳並帶有回覆、觸發作業的撥號盤 |
vault-rotate.ts、firewall-filter.ts、zero-trust.ts | 中等 | 使用插槽模式的密鑰交換、被拒絕的資料包彈回、每個請求都被蓋章 |
workload-scheduler.ts、autoscale.ts、health-check.ts | 中等 | 從一個小型規則表中進行放置、為新節點騰出空間、一個被拉出並重新加入的節點 |
blue-green.ts、snapshot-restore.ts | 中等 | 重新定位的線路、沿時間軸向後滑動的游標 |
embedding-clusters.ts、training-loss.ts、recs-rerank.ts、ab-test.ts | 中等 | 點聚整合群、下降的 prism 條、重新排序的槽架、帶有勝出者的分割 |
data-erasure.ts、rag-answer.ts | 複雜 | 一個請求扇形展開到六個系統並帶有計數器、檢索到的卡片飛入模型並逐字輸入答案 |
hero.ts | 複雜 | 一個提示以 1、5、15 和 40 個的層級發展一個系統:轉盤旋轉進入和加權掉落、從下方滑出的輸出、彈出、一個伴隨網格填充掉入的池、無盡的請求、在靜止時朝向滑鼠的傾斜;透過 fill 和使用 <use> 的重複拼貼保持低成本;僅使用 SVG text(),因此 exportSvg() 可作用於它 |
funnel-live.ts、billing-cycle.ts、parcel-delivery.ts、test-shards.ts | 中等、複雜 | layout: 'wide':3:1 條狀從左到右閱讀(逐漸變窄的階段、一個無跳躍移動的月份條、運送盒子的車輛、在車道中競速並合併的碎片的 |
kanban-move.ts、stock-sync.ts、diff-merge.ts、refund-flow.ts | 簡單到複雜 | layout: 'diagonal':一個沿著一條等角軸線移動的卡片或令牌、閘門變綠、以第二種顏色沿相同路徑返回的退款 |
live-cursors.ts、two-approvals.ts、design-comments.ts、translate-six.ts | 簡單、中等 | layout: 'panels':彼此面對的直立 panel();輸入的游標、解鎖鎖頭的兩個勾選標記、別針和討論串、一個包含六種語言(帶有泰語和日語字體堆疊)的訊息 |
cms-publish.ts、sensor-gateway.ts、smart-home.ts、currency-convert.ts | 簡單、中等 | layout: 'radial':一個副本到五個頻道、讀數進入然後一層地板波出去、設備依次改變狀態、一個脈衝沿環形線路走一圈 |
ota-rollout.ts、build-cache.ts、calendar-booking.ts、load-slider.ts | 簡單到複雜 | layout: 'field':波穿過設備場,帶有暫停和重試、從依賴表計算出的快取命中、跳過衝突的會議;load-slider 是一個拖動控制項(真實的範圍輸入、滑鼠、觸控和按鍵),在環境流量持續移動時尋找循環 |
video-transcode.ts、image-resize.ts、fraud-gauge.ts、data-lineage.ts、drill-down.ts | 簡單到複雜 | layout: 'exploded':分離並編碼的版本、剝離的大小、累積超過一條線的風險層、向下穿過層次點亮的上游路徑;drill-down 透過一個真實按鈕,尋找該部分的循環片段,將一部分打開成其層次 |
materials-*.ts、material-gallery.ts | 六個系列(金屬、玻璃、自然、科技圖案、復古、生成式)中的 49 種側面飾面,作為 (scene) => Material 工廠,每個系列一個無標籤的圖庫。生成式飾面接受一個種子。複製一個飾面而非發明一個 |
一次性設定:cd <this dir>/scripts && bun install && bunx playwright install webkit(螢幕截圖使用已安裝的 Google Chrome 作為 Chromium)。類型檢查:scripts/node_modules/.bin/tsc -p <this dir>。
1. 繪製前先進行故事板
首先開啟 recipes.md。它包含所有經過測量的動作和構圖模式(到達、連接、流、比較、選擇、空閒、互動、緩動、表面),每個都有其數字和工具套件調用。從這些配方中組成故事,並保持它們的時間安排。
首先將故事寫成時間軸表格:觀眾應該理解什麼、展示它的 3 到 6 個節拍,以及對於每個節拍,哪個元素移動、如何移動、何時移動以及移動多久。每個節拍一個想法。一個需要一段文字來解釋的節拍,是兩個節拍或錯誤的圖片。
經過驗證的節拍形狀,附有測量時間:
| 節拍 | 發生什麼事 | 時間安排 |
|---|---|---|
| 到來 | 碎片逐個落下,稍微過衝,然後穩定 | dropIn:每個 0.55 秒,83 毫秒間隔,46 像素下落 |
| 交接 | 一個拼貼的幽靈輪廓從一個物件滑向另一個物件 | 4 個幽靈,間隔 0.22 秒,每個 1.2 秒,ease.io |
| 傳遞 | 一個短光脈衝沿線路運行;目標在到達時閃爍 | 270 像素/秒,脈衝約為線路的 0.12 至 0.16,上升 0.1 秒,淡出 0.6 秒 |
| 揭露層次 | 一個實心盒子變成透明;內部層次逐個點亮,並附帶標籤 | 開啟 0.23 秒,保持約 1 秒,關閉 0.2 秒,下一個在最後一個結束時開始 |
| 旋轉選擇 | 一個堆疊向上移動一個槽位;活動卡片變成強調色 | 每 3 秒移動 0.9 秒,慢速啟動,長時間緩出 |
| 填充與清空 | 一個網格逐個拼貼填充,保持,然後回落到空 | 進入時間隔 7 幀(0.12 秒),離開時間隔 2 幀 |
| 拉力 | 當一條線路縮短或增長時,它所連接的盒子會沿線路方向被拉動約 6 像素,保持,然後緩慢恢復,使線路看起來有張力 | 拉動 0.23 秒,保持 0.25 秒,返回 0.65 秒並帶有長時間緩出 |
| 懸停升起 | 一個拼貼上升 16 像素並呈現其品牌顏色,其名稱出現 | slab({ hover }):開啟 270 毫秒,關閉 325 毫秒,ease.out |
連接規則:一條線路僅在其兩端都在螢幕上時才顯示。連接到中途到來之物件的線路,會隨著其脈衝繪製自身(wire({ dash: '1 1' }),然後 at(id, 'strokeDashoffset', t, d, 1, 0, ease.linear)),並與它所連接的東西一起離開。切勿將這樣的線路放在介紹中。線路永遠不會越過一個盒子的頂面:在它所連接的物件之前添加它(因此它們會覆蓋它),並從盒子的邊緣開始,而不是中心。
循環規則:最後一幀等於第一幀。任何在循環期間變化的東西,都會在循環結束前恢復。僅在介紹中的動畫進入 intro();循環在介紹結束時開始,並且介紹必須以與循環開始相同的畫面結束。一個沒有介紹的元素,在介紹期間顯示循環的起始值,這是其循環片段的最後一個 to 值。
繪製順序是固定的:稍後添加的總是覆蓋先前添加的。會改變誰覆蓋誰的動畫(例如,一張卡片從堆疊頂部移動到底部)應繪製為槽位。每個元素都是一個槽位,移動一步並像其鄰居一樣淡出或重新著色,並且每個片段都從槽位的靜止值重新開始,因此所有槽位同時跳回,而畫面看起來相同。為每個槽位提供每個選項一個標籤,並交叉淡化在活動槽位旁顯示哪個。
2. 選擇佈局,然後先為手機構圖
每個範例在其 layout 中命名其構圖:wide(一個從左到右閱讀的長條;其寬場景大約為 3:1,例如 1200x400,斷點為 720,展示區將其顯示為全寬卡片)、diagonal(沿等角軸線的長行)、panels(彼此面對的直立 panel())、radial(一個核心,周圍有環形排列的東西)、field(從高處看到的許多拼貼;略陡的 ratio 有幫助)、exploded(垂直分離的層次)或 object(一個或兩個物件,用於功能列表或空狀態)。在繪製前選擇故事需要的佈局;當它們變化時,場景頁面閱讀起來更好。
一個觀眾可以遊玩的場景(interaction: 'play' | 'drag' | 'explore')會在 art() 的 head 中提供一個小型內聯模組腳本:label({ html }) 中的真實 <button> 或 <input type="range">、用於滑鼠和觸控的指標事件(拖動控制項上為 touch-action: none)、也支援按鍵、一個 MutationObserver 以綁定稍後掛載的場景、在觀眾操作時暫停循環並在空閒幾秒後恢復,以及一個在沒有輸入時仍然講述故事的循環(feature-flags.ts、load-slider.ts、drill-down.ts)。
展示頁面和登陸頁面是在手機上開啟的。構建兩個場景並使用 responsive(compact, wide) 包裝:
- compact:約 390 寬,縱向或正方形(390x420 到 390x640)。故事從上到下堆疊。以 390 單位設計,因此大小為 13 的標籤在手機上為 13 像素。絕不低於 13。
- wide:約 900 到 1200 寬,橫向,故事從左到右。