Communitygithub.com

MooT020/kinetic-typography

製作動態字體排版影片:讓文字本身成為視覺主體,透過撞擊、拉伸、堆疊、分割與替換等動作,配合旁白或音樂節拍,輸出為 9:16 或 16:9 的 MP4 影片。

kinetic-typography 是什麼?

這是一個用於製作動態字體排版影片的 AI 技能,適合純文字解說、鉤子片段、片頭序列、引言、歌詞或詩詞影片、公告,以及逐字字幕等需要文字本身就是主要設計元素的場景。它支援 HyperFrames + GSAP 和 Remotion 兩種平台,但預設使用 HyperFrames + GSAP。使用者提供一句核心訊息,將腳本拆分為每個 1–4 個詞的短語,為每個短語標記一個英雄詞,指定能量高峰和結尾鎖定畫面。技能會根據節奏來源(語音、音樂或固定節拍)建立時間軸,並使用預先定義的動作詞彙(如撞擊、堆疊、拉伸、字母級聯、翻轉替換、分離和退出)來讓文字動起來。風格規則強制文字佔據畫面寬度的 50–80%,一次只顯示一個想法,每個文字進場都必須伴隨動作,對比強烈,並且每個短語只有一個英雄詞。預設樣式為深色背景 (#111111)、淺色文字 (#f4f1ea) 和一個強調色 (#ff4d2e)。技能也包含詳細的運動紀律(如因果關係、主要運動方向、緩動函數)、眼睛吸引技巧(填滿畫面、顏色翻轉、紋理深度、相機能量)以及避免的常見失敗模式。最終輸出前需要通過快照驗證和自我評分,確保品質。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MooT020/takhayyal-video-skills/tree/HEAD/plugins/motion-design-styles/skills/kinetic-typography

在你喜歡的 AI 中提問

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

說明文件

動態字體排版 (Kinetic Typography)

製作影片時,讓文字本身成為畫面。一個短語出現,填滿畫面,被重擊後被下一個短語替換,並配合節奏。應該感覺大膽、快速且富有音樂性,就像會動的海報。

選項

選項預設值備註
格式格式垂直 1080×1920或橫向 1920×1080、正方形 1080×1080
時間10–20 秒與旁白或歌曲段落匹配
風格強勁calm(0.4 秒的滑入淡出,較少翻轉)、punchy(預設)、chaotic(全大寫、撞擊、抖動、每隔一個短語就翻轉)
節奏固定節拍或旁白單詞時間、或音樂節拍

簡報 (規劃前回答)

  1. 觀眾應該記住的一句話是什麼?
  2. 將腳本拆分為每個 1–4 個詞的短語;為每個短語標記一個英雄詞。
  3. 能量高峰在哪裡(獲得顏色翻轉的短語)?
  4. 結尾鎖定畫面是什麼(標題、呼籲行動、帳號名稱)?

閱讀表 (與節拍清單一起撰寫)

對於每個節拍,寫下觀眾必須理解的一件事(「機器人不能飛」、「快取未命中」)以及它保持可讀的時間。一次只閱讀一個。文字地板:一個標籤至少停留 0.8 秒,一個句子一旦完全顯示在螢幕上,每個詞大約 0.3 秒。如果文字不符合時間要求,則減少詞彙而不是加快速度。

運動紀律

  • 運動是有因果關係的:每個動作在螢幕上都有原因(點擊、掉落、資料包到達),下一個動作從中產生。
  • 為整個影片選擇一個主要運動方向並保持不變。使用其他方向來表達意義:向上表示成功或結論,縮小表示「這是整體情況」。
  • 一個場景的退出為下一個場景的進入做好準備:相同的軸、相同的方向、相似的速度。在變化之間攜帶一個物件,而不是交叉淡化。
  • 在圖表、圖表或文字上不要有無聊的填充動畫(浮動、呼吸、搖擺)。如果沒有東西應該移動,就保持靜止;一次刻意鏡頭移動就足夠了。
  • 緩動:順暢的 ease-out 用於進入(power2.out 到 power4.out),較快的 ease-in 用於退出(約為進入時間的 75%)。進入最多 0.8 秒。群組的總間隔最多 0.5 秒。過衝 (back.out) 僅用於小的彈跳;不要用彈性或彈跳。
  • 在重要時刻,一次只移動一個東西;眼睛一次只能跟隨一個。
  • 在關鍵時刻之前,加入 0.3–0.75 秒的靜止節拍。

風格規則 (總是)

這些造就了這種風格;無論顏色或單詞如何,都保持它們。

  1. 文字佔據畫面寬度的 50–80%,因為小文字看起來像字幕,而不是視覺主體。
  2. 一次只在螢幕上顯示一個想法:1–4 個詞,最多 3 行。其他所有內容都已離開。
  3. 每個單詞都伴隨著一個動作到達(撞擊、滑動、拉伸、遮罩揭示、翻轉)。沒有任何東西只是淡入。
  4. 變化落在節奏上:每 0.25–0.7 秒一個新單詞或動作,關鍵詞落在節拍或重音音節上。
  5. 對比極端:近黑色在近白色上,或反之,加上僅用於關鍵詞的一個強調色。
  6. 每個短語都有一個英雄詞:最大、強調色、或與其他詞語移動方式不同。

預設值 (可隨意更改)

字型、顏色、文字、時間和背景。如果使用者提供品牌字型或顏色,則以他們的為主。優先級:使用者的品牌或規格 > 明確要求 > 這些預設值。

  • 背景 #111111,文字 #f4f1ea,強調色 #ff4d2e。根據能量需求,每個短語可在深色淺色或淺色深色之間翻轉。
  • 顯示字型:一個粗重的壓縮字體 (Anton, Bebas Neue 或 Archivo Black),英雄詞使用全大寫。輔助字型:一個粗重無襯線字體 (Inter 800) 用於小的連接詞。
  • 尺寸:9:16 中英雄詞 180–320 px;小詞 60–90 px。

設定 (一次性)

node -v                      # 需要 22+
ffmpeg -version
npx hyperframes init my-video --non-interactive --resolution portrait && cd my-video   # 16:9 使用 landscape

在現有空資料夾內部,使用 npx hyperframes init . --non-interactive --resolution portrait,然後將生成的 index.html 替換為下面的範本。

建構前決定

在編寫程式碼之前,用一行向使用者回報這些。

  1. 長寬比: 9:16 (1080×1920) 或 16:9 (1920×1080)。預設為 9:16。
  2. 文字和時間: 將腳本拆分為 1–4 個詞的短語。有旁白時,從其單詞時間戳記獲取時間 (npx hyperframes transcribe voice.wav)。沒有旁白時,每個短語給 0.6–1.4 秒,英雄詞給更長時間。
  3. 節奏來源: 語音 (落在重音音節上)、音樂 (落在節拍上;npx hyperframes beats music.mp3) 或固定節拍 (例如 120 BPM = 每 0.5 秒一個節拍)。
  4. 每個短語的英雄詞以及它獲得的動作。
需求HyperFrames + GSAP (預設)Remotion
單詞和字母移動、遮罩、拉伸是是
從語音或節拍資料獲取時間是 (標籤)是 (幀)
僅需 Node 和 FFmpeg 即可執行是是
授權免費3 人以下免費,以上付費

動作詞彙

動作操作方式 (GSAP)使用時機
撞擊from {scale: 2.6, opacity: 0},expo.out,0.22 秒,加上 6 px 螢幕抖動 0.1 秒英雄詞
滑動堆疊行數 from {yPercent: 110} 在 overflow:hidden 遮罩內,間隔 0.06 秒,power4.out 0.35 秒列表、兩行短語
拉伸scaleY: 1.6 → 1,transformOrigin: bottom,back.out(3) 0.3 秒強調、動詞
字母級聯字母 from {y: -120, rotation: -20, opacity: 0},間隔 0.025 秒,back.out(2)名字、標題
翻轉替換舊詞 rotationX: 90 出去 (0.12 秒),新詞 rotationX: -90 → 0 進來 (0.18 秒)X 變成 Y
分離單詞兩半滑開 x: ±140 以在之間顯示一個較小的詞對比
退出yPercent: -110 或 scale: 0.7, opacity: 0,0.15 秒,power2.in每個短語離開

退出很快 (0.12–0.18 秒),進入比你認為的更快 (0.18–0.35 秒)。每個短語落地後至少保持 0.35 秒,以便可以閱讀。

使其引人注目

  • 填滿畫面。 將文字調整到畫面寬度,而不是字型大小。250+ px 的壓縮字體是這種風格。
  • 顏色翻轉: 在 2–3 個關鍵短語上,背景在深色和淺色之間切換(或切換到強調色),作為節拍上的硬切。
  • 紋理和深度: 一個微妙的顆粒疊加層、一個柔和的陰影或偏移複本 (強調色,偏移 10–14 px) 放在英雄詞後面,以及一個偶爾出現的巨大淡色背景詞。
  • 鏡頭能量: 整個舞台緩慢縮放 2–4%,英雄擊中時放大 (scale 1.06,持續 0.1 秒),以及撞擊時 6–10 px 的抖動。
  • 文字搭配形狀: 劃入的底線、在單詞後面彈出的圓圈、箭頭。強調色,扁平。
  • 以鎖定畫面結尾: 標題或呼籲行動,完整編排,保持 1–1.5 秒,與開場幀匹配以便循環。
  • 品質關卡: 如果任何快照顯示小文字居中在純色背景上,那就不算完成。

建構:範本

<!doctype html><html><head><meta charset="UTF-8">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Anton&family=Inter:wght@800&display=swap">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<style>
html,body{margin:0;width:1080px;height:1920px;overflow:hidden;background:#111}
#root{width:100%;height:100%;position:relative;overflow:hidden}
#stage{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}
.phrase{position:absolute;left:60px;right:120px;top:270px;bottom:670px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:0}
.line{overflow:hidden;line-height:.92;padding:0 8px}
.w{display:inline-block;font-family:Anton,sans-serif;text-transform:uppercase;color:#f4f1ea;white-space:nowrap}
.hero{color:#ff4d2e}
.flip .w{color:#fff}.flip .hero{color:#111}
.small{font-family:Inter,system-ui,sans-serif;font-weight:800;text-transform:none}
#bg{position:absolute;inset:0;background:#111}
</style></head>
<body><div id="root" data-composition-id="main" data-start="0" data-duration="8" data-width="1080" data-height="1920">
<div id="bg"></div><div id="stage"></div></div>
<script>
// [時間, 行數, 動作名稱, 英雄詞索引, 是否翻轉背景?]  -- 編輯此列表;時間以秒為單位
const PHRASES = [
  [0.2, ["WORDS"], "slam", 0],
  [1.0, ["can", "MOVE"], "stack", 1],
  [2.0, ["LIKE", "MUSIC"], "stretch", 1, true],
  [3.2, ["one", "idea"], "stack", 1],
  [4.2, ["AT A", "TIME"], "slam", 1],
  [5.4, ["KINETIC", "TYPE"], "cascade", 1],
];
const END = 8, W = 1080 - 180;
// 僅在網頁字型載入後測量文字,然後建構並註冊時間軸
document.fonts.ready.then(() => {
const tl = gsap.timeline({ paused: true });
const stage = document.getElementById("stage");
PHRASES.forEach(([t, lines, move, hero, flip], i) => {
  const p = document.createElement("div"); p.className = "phrase" + (flip ? " flip" : ""); stage.appendChild(p);
  const words = lines.map((txt, k) => {
    const line = document.createElement("div"); line.className = "line";
    const w = document.createElement("span"); w.className = "w" + (k === hero ? " hero" : "") + (txt === txt.toLowerCase() && k !== hero ? " small" : "");
    w.textContent = txt; line.appendChild(w); p.appendChild(line);
    w.style.fontSize = "100px"; const fit = Math.min(420, (100 * W) / w.getBoundingClientRect().width); // 填滿寬度
    w.style.fontSize = (w.classList.contains("small") ? Math.min(fit, 110) : fit) + "px";
    return w;
  });
  gsap.set(p, { autoAlpha: 0 });
  tl.set(p, { autoAlpha: 1 }, t);
  if (move === "slam") tl.from(words, { scale: 2.6, opacity: 0, duration: 0.22, ease: "expo.out", stagger: 0.08 }, t)
                         .fromTo("#stage", { x: -10 }, { x: 10, duration: 0.04, yoyo: true, repeat: 3, ease: "none" }, t + 0.18).set("#stage", { x: 0 }, t + 0.34);
  if (move === "stack") tl.from(words, { yPercent: 110, duration: 0.35, ease: "power4.out", stagger: 0.07 }, t);
  if (move === "stretch") tl.from(words, { scaleY: 1.7, opacity: 0, transformOrigin: "50% 100%", duration: 0.3, ease: "back.out(3)", stagger: 0.08 }, t);
  if (move === "cascade") words.forEach((w, k) => {
    const letters = Array.from(w.textContent); w.textContent = "";
    letters.forEach((c) => { const l = document.createElement("span"); l.style.display = "inline-block"; l.textContent = c; w.appendChild(l); });
    tl.from(w.children, { y: -140, rotation: -20, opacity: 0, duration: 0.4, ease: "back.out(2)", stagger: 0.03 }, t + k * 0.15); });
  const next = PHRASES[i + 1] ? PHRASES[i + 1][0] : null;
  if (flip) tl.set("#bg", { background: "#ff4d2e" }, t).set("#bg", { background: "#111" }, next || END);   // 在關鍵短語上翻轉顏色
  if (next) tl.to(words, { yPercent: -110, opacity: 0, duration: 0.15, ease: "power2.in", stagger: 0.03 }, next - 0.17)
               .set(p, { autoAlpha: 0 }, next);
});
tl.fromTo("#root", { scale: 1 }, { scale: 1.04, duration: END, ease: "none" }, 0);         // 慢速漂移
tl.set({}, {}, END);
window.__timelines["main"] = tl;
});
</script></body></html>

編輯 PHRASES 以更改文字和時間。通過從動作詞彙中添加 if (move === ...) 分支來添加動作。有旁白時,將 <audio> 元素放在根元素中,並從其轉錄中獲取短語時間。

向使用者曝光的參數

aspect · phrases (時間, 行數, 動作, 英雄) · fontDisplay · fontSupport · colors (bg, text, accent) · tempo or audio · flipPhrases (背景翻轉的位置) · endLockup。

避免

  • 淡入和緩慢漂移作為主要動作。看起來像簡報。
  • 一次在螢幕上超過 4 個詞或 3 行。
  • 多個強調色。只用一個強調色,且僅在英雄詞上使用。
  • 細體或淺色字重,或腳本字體用於英雄詞。
  • 文字在 9:16 畫面的底部 670 px 或頂部 270 px 內(平台 UI 會遮蓋它們)。
  • 隨機或每次渲染不同的變化。每個值必須來自時間軸。

失敗模式

看起來像原因修復方式
簡報淡入淡出、長時間停留、每次動作相同從詞彙中變化動作;進入時間低於 0.35 秒;退出時間 0.15 秒
字幕,非設計文字太小,居中在空白空間將文字大小調整到畫面寬度;使用壓縮字體;250+ px
混亂、難以閱讀一次太多文字或動作一次只顯示一個短語;一個英雄詞;落地後保持 0.35 秒
不跟拍短語時間是猜測的使用轉錄或節拍時間;讓擊中提前 2–3 幀
文字被遮罩裁剪行高對於字體來說太緊line-height 0.92–1.0,並為 .line 添加內邊距;檢查快照
渲染中字體錯誤字體未加載在 <head> 中使用 Google Fonts <link>;檢查快照是否顯示壓縮字體

最終渲染前驗證

npx hyperframes check
npx hyperframes snapshot --at <每個短語落地後 0.3 秒的時間戳記, 加上一個退出中途>
npx hyperframes render -o renders/<name>.mp4
ffprobe -v error -show_entries stream=width,height,r_frame_rate:format=duration -of csv=p=0 renders/<name>.mp4

查看每個快照和 snapshots/contact-sheet.jpg,並確認:

  • 每個短語佔據寬度的 50–80%,並且在手機尺寸下可讀
  • 每個短語一個英雄詞,使用強調色或不同的動作
  • 沒有任何一幀上有兩個短語重疊
  • 所有文字都在該長寬比的安全區域內
  • 結尾鎖定畫面至少保持 1 秒,並與開場匹配
  • 尺寸和時間與要求相符

最終渲染前自我批評

對快照表進行 1–10 評分:鉤子 (第一秒)、手機寬度可讀性、運動品質、構圖、準確性,以及「靜音測試」(觀眾能否在沒有聲音的情況下用一句話總結它?)。只修復得分 7 或以下的項目,重新拍攝快照,並重複直到所有項目得分 8 或以上。如果可以,讓一個獨立的唯讀評估來評分,這樣你不是在評判自己的工作。

不要而應該
在兩個繁忙的版面之間交叉淡入淡出先退出舊內容,然後引入新內容,或攜帶一個物件橫跨
讓多個東西同時移動錯開時間,一次一個焦點點

Individual skills in this repo

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

相關技能