Communitygithub.com

倒數計時影片

此技能應在用戶要求製作倒數計時影片時使用。

倒數計時影片 是什麼?

這個技能的核心原則是:倒數剩餘時間必須從權威時鐘(例如 Remotion 的 `useCurrentFrame()` 或網頁的 `performance.now()`)派生,絕不能使用 `setInterval` 手動遞減。這種設計消除了因頁面節流或渲染卡頓導致的時間誤差累積,確保倒數在精確的影格上歸零,無漂移。支援的場景包括:直播「即將開始」畫面、產品發售或限時搶購的倒數計時,以及需要精確結束時間的任何動畫計數器。技能涵蓋了 Remotion 渲染影片(使用 `useCurrentFrame()`)和即時網頁(使用 `requestAnimationFrame` 與 `performance.now()`)兩種實現方式,並提供了 `mm:ss`、`hh:mm:ss`、`dd:hh:mm:ss` 等多種時間格式,以及數位滾輪翻轉動畫和無縫循環背景的最佳實踐。最後,透過輸出清單和驗證腳本,確保成品在每個檢查影格上都能達到影格級別的精確度。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/iart-ai/tiktok-video-skills/tree/main/skills/countdown-video

在你喜歡的 AI 中提問

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

說明文件

倒數計時影片 是做什麼的?

製作出一個能在精確時刻結束的倒數計時。整個技巧的核心只有一個概念:從影格(或單調遞增的時間戳)推算出剩餘時間,然後格式化並動畫顯示那個數字——絕對不要用 setInterval 來倒數。涵蓋直播「即將開始」畫面、產品發售/限時搶購倒數計時,以及循環背景。

使用時機

  • 直播「即將開始」/「馬上回來」畫面,搭配 5–10 分鐘的計時器。
  • 產品發售和限時搶購倒數計時(dd:hh:mm:ss 格式,倒數至目標日期;營造急迫感時使用 mm:ss 格式)。
  • 任何需要數字在精確影格上歸零的動畫計數器。

唯一規則:從時鐘驅動數字,絕不使用 tick 計數

使用 setInterval(fn, 1000) 的倒數計時會漂移——回呼函數至少會延遲 1000 毫秒才觸發,不會完全精準,而且當分頁被節流或渲染影格變慢時,誤差會累積。常見的狀況是每分鐘大約會損失 1 秒。修正方法對於渲染影片和即時網頁都相同:將剩餘時間計算為權威時鐘的純函數,這樣即使某個影格延遲了,下一個影格也會自動修正,而不是累積誤差。

情境權威時鐘剩餘時間
Remotion 渲染useCurrentFrame()總時間 − 影格/影格率
網頁(即時頁面)performance.now() 或 Date.now()目標毫秒數 − 現在時間
倒數至固定日期Date.now()目標時間戳 − Date.now()

絕不要將剩餘時間儲存在狀態中並遞減它。請在每個影格重新計算。

影格精確的倒數計時(Remotion → MP4)

useCurrentFrame() 是確定性的:影格 N 總是在 N/fps 秒時被渲染。從總時間減去它,計時器天生就是無漂移的。

import { useCurrentFrame, useVideoConfig } from "remotion";

export const Countdown: React.FC<{ durationInSeconds: number }> = ({
  durationInSeconds,
}) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  // 剩餘秒數,限制在 0 以上——使用 ceil 讓「1」在整個秒數內顯示
  const elapsed = frame / fps;
  const remaining = Math.max(0, Math.ceil(durationInSeconds - elapsed));

  return <div style={{ fontVariantNumeric: "tabular-nums" }}>{formatMMSS(remaining)}</div>;
};

durationInSeconds 是一個屬性,不是常數——同一個組合可以透過改變一個輸入參數,來渲染一個 5 分鐘的串流計時器或一個 30 秒的銷售計時器。將組合的 durationInFrames 設定為 durationInSeconds * fps,這樣影片的長度就會剛好等於倒數時間。

網頁計時器(即時頁面,requestAnimationFrame)

對於即時頁面,使用 performance.now() 驅動一個 requestAnimationFrame 循環,並且只在顯示的秒數改變時才重新繪製(不需要為了每秒更新 1 次的數字而每秒鐘執行 60 次無謂的運算):

const start = performance.now();
let lastShown = -1;
function tick(now) {
  const remaining = Math.max(0, Math.ceil(duration - (now - start) / 1000));
  if (remaining !== lastShown) { render(remaining); lastShown = remaining; }
  if (remaining > 0) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);

倒數至固定日期的實現方式相同,只是改用 Date.now():Math.max(0, target - Date.now())。請參閱 references/countdown-cookbook.md 以取得包含時區處理的完整日期倒數版本。

時間格式化

使用除法與模數將剩餘秒數整數分解成各個欄位,然後補零。根據持續時間選擇合適的格式——當分鐘數才是重點時,絕不要顯示 00天 00時 05分。

const pad = (n) => String(n).padStart(2, "0");
const formatMMSS = (s) => `${pad(Math.floor(s / 60))}:${pad(s % 60)}`;
const formatDHMS = (s) => {
  const d = Math.floor(s / 86400), h = Math.floor((s % 86400) / 3600);
  const m = Math.floor((s % 3600) / 60), sec = s % 60;
  return `${pad(d)}:${pad(h)}:${pad(m)}:${pad(sec)}`;
};
格式用途隱藏欄位的時機
mm:ss直播「即將開始」、短時計時器(<1小時)—
hh:mm:ss限時搶購、同日活動當 dd 為 0 時隱藏
dd:hh:mm:ss產品發售 / 多日倒數隱藏前導零單位以簡潔呈現

總是設定 font-variant-numeric: tabular-nums(或使用等寬數字字型),這樣數字才能維持等寬,版面配置也不會在數字變化時跳動。

數字滾輪/翻牌轉場

單純的文字數字切換看起來很廉價;滾動的「里程表」外觀能提升倒數計時的質感。將每個數字渲染成 0–9 的垂直長條,並透過 -數字 × 數字高度 平移。動畫位置,而不是文字內容,這樣變化才會是平滑的滑動,而不是突然的彈出。

.reel { height: 1em; overflow: hidden; }
.reel .col { transition: transform .45s cubic-bezier(.22,1,.36,1); }
/* col 包含堆疊的 0..9;透過 -value em 偏移 */

在 Remotion 中,透過影格來驅動長條偏移,而不是 CSS 過渡(這樣才能確保確定性)。翻牌卡樣式(上半部向下摺疊覆蓋下半部)和每位數的滾輪效果,在 references/digit-flip.md 中都有詳細說明並附有可運行的元件。

循環背景

串流畫面會持續數分鐘,因此背景必須無縫循環。建立一個週期能均分時間線的動畫(例如一個週期為 loopFrames 的漸層或粒子飄移),並以 frame % loopFrames 的方式讀取它,這樣在循環點就不會有可見的接縫。保持低對比度和緩慢速度——數字才是主角;背景只是氛圍。可在 references/countdown-cookbook.md 中找到實作方法。

輸出檢查清單

  • 剩餘時間每個影格都從 useCurrentFrame() / performance.now() 重新計算——沒有 setInterval 遞減。
  • 計時器在精確的目標影格上顯示 00:00;durationInFrames === durationInSeconds × fps。
  • 持續時間是一個輸入屬性,不是硬編碼的常數。
  • 數字使用 tabular-nums;版面配置不因數字變化而移動。
  • 格式符合時間跨度(mm:ss vs dd:hh:mm:ss);當前導零單位造成干擾時將其隱藏。
  • 背景循環週期均分時間線;循環點無接縫。

交付與驗證(渲染靜止幀 → MP4)

打包好的輔助腳本(scripts/):使用 scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png 拼貼你的靜止幀,然後使用 scripts/probe-mp4.sh out.mp4 [WxH] [fps] 驗證編碼。參閱 scripts/README.md。

倒數計時影片以 Remotion 組合(<Composition> + zod schema + defaultProps,durationInSeconds 為屬性)的方式交付——計時器是 useCurrentFrame() 的純函數,絕不使用 setInterval / Date.now() / Math.random()。設定 durationInFrames = durationInSeconds * fps。交付產物 = out/*.mp4 + 專案。預設為 9:16 垂直(1080×1920)。

驗證循環 — 渲染靜止幀 → 檢查 → 編碼。 整個重點在於影格精確度,所以在編碼之前,請先檢查精確影格上的數字值。

# 開始 / 中間 / 結束的靜止幀 —— 使用實際傳入的屬性(你將渲染的真實持續時間)
npx remotion still Countdown out/f-start.png --frame=0   --props='{"durationInSeconds":300}'
npx remotion still Countdown out/f-mid.png   --frame=N   --props='{"durationInSeconds":300}'
npx remotion still Countdown out/f-end.png   --frame=L   --props='{"durationInSeconds":300}'  # L = durationInFrames-1

# 檢查每個 PNG —— 數字必須完全符合 frame/fps 的數學計算:
#  - 在影格 N,顯示值 = ceil(durationInSeconds - N/fps)(例如 300 秒 @ 30fps,影格 4500 → 150 秒 → "02:30")
#  - 最後一個影格顯示 00:00(或結束狀態維持);無漂移,無差一秒的誤差
#  - tabular-nums:版面配置在影格之間不跳動
#  - 9:16 安全區域:計時器 + 品牌區塊需避開頂部約 12% 和底部約 20-35% 的 UI 以及右側的操作欄

npx remotion render Countdown out/countdown.mp4 --props='{"durationInSeconds":300}'   # 確認數字無誤後再編碼
npx remotion render Countdown out/demo.gif --codec=gif                                 # README 展示片段

使用 npx remotion compositions 確認 durationInFrames/fps,然後計算你想檢查的任何秒數所對應的影格。

完成前:

  1. 靜止幀在影格 0、中間和最後一個影格都能乾淨渲染——沒有錯誤。
  2. 影格 N 的數字值完全等於 ceil(durationInSeconds - N/fps);最後一個影格在目標影格上顯示 00:00(無漂移 / 無差一秒的誤差)。
  3. 計時器 + 品牌區塊在每個檢查影格上都保持在 9:16 安全區域內。
  4. 僅由影格驅動——沒有 setInterval / Date.now() / 計時器;使用了 tabular-nums 確保版面穩定。
  5. 傳入的 durationInSeconds(不僅是 defaultProps)能正確渲染;已編碼 MP4,GIF 為選項。

參考檔案

  • references/countdown-cookbook.md — 完整的可執行 Remotion <Countdown>(可設定持續時間、mm:ss / dd:hh:mm:ss、結束狀態維持)、包含時區處理的倒數至固定日期網頁元件、「即將開始」串流畫面版面(品牌區塊 + 社交帳號 + 循環漸層背景),以及無縫循環背景模式。
  • references/digit-flip.md — 兩種確定性的數字動畫元件:里程表滾輪(由影格驅動的垂直 0–9 長條)和翻牌卡(上半部摺疊)效果,以及它們各自適合用於產品發售、銷售或串流的時機。

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

相關技能