Communitygithub.com

countdown-video

该技能用于制作精确到帧的倒计时视频,适用于直播开场、发布倒计时等场景。

countdown-video 是什么?

这是一个用于生成帧精确倒计时视频的AI技能,基于Remotion框架构建。其核心思想是从帧编号(或单调时间戳)推导剩余时间,然后格式化和动画显示该数字,绝不使用 `setInterval` 递减计数器。它支持直播“即将开始”屏幕、发布/销售倒计时以及循环背景。技能文档详细解释了为何时钟驱动优于计数器(防止漂移),并提供了Remotion(生成MP4)和Web(实时页面)两种实现方案。文档还涵盖了时间格式化、数字翻转过渡动画、循环背景设计以及完整的输出检查清单和验证流程。此技能适用于需要倒计时视频且要求绝对帧准确的开发者和内容创作者,支持9:16竖屏格式。

兼容平台~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)。
  • 任何必须在精确帧上归零的动画计数器。

唯一规则:数字由时钟驱动,绝不使用时钟滴答

setInterval(fn, 1000) 倒计时会漂移——回调至少晚1000毫秒触发,永远不会精确触发,并且在标签页被节流或渲染帧缓慢时会堆积。大约每分钟会丢失1秒。修复方法对于渲染的视频和实时Web都是一样的:将剩余时间计算为一个权威时钟的纯函数,这样延迟的帧会在下一帧自我修正,而不是累积误差。

上下文权威时钟剩余时间
Remotion 渲染useCurrentFrame()total − frame/fps
Web(实时页面)performance.now() 或 Date.now()targetMs − now
固定日期Date.now()targetTimestamp − 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 是一个属性(prop),而不是常量——同一个组件通过更改一个输入即可渲染5分钟的流计时器或30秒的销售计时器。将组件的 durationInFrames 设置为 durationInSeconds * fps,以便视频的长度与倒计时完全一致。

Web 计时器(实时页面,requestAnimationFrame)

对于实时页面,使用 performance.now() 驱动一个 rAF 循环,并且仅在显示的秒数发生变化时重新绘制(不需要为了一个1Hz的数字每秒执行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。

时间格式化

使用除法和取模将剩余秒数整数分解为各个字段,然后补零。选择适合持续时间的格式——当只有分钟重要时,绝不显示 00d 00h 05m。

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流“即将开始”,短计时器(<1h)—
hh:mm:ss闪购,当日活动dd 为0时隐藏
dd:hh:mm:ss发布 / 多日倒计时为更清晰显示隐藏前导零的单元

始终设置 font-variant-numeric: tabular-nums(或使用等宽数字字体),以便数字保持等宽,布局不会在数字变化时抖动。

数字滚动/翻转过渡

平面数字切换显得廉价;滚动的“里程表”外观能营造倒计时的感觉。将每个数字渲染为0-9的垂直条带,并通过 -digit × digitHeight 进行平移。动画化位置而不是文本内容,这样变化是滑行而不是弹出。

.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)  (例如 300s @ 30fps,帧 4500 → 150s → "02:30")
#  - 最后一帧显示 00:00(或结束状态保持);无漂移,无1秒偏差
#  - 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(无漂移/1秒偏差)。
  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、结束状态保持)、带时区处理的固定日期倒计时Web组件、“即将开始”流屏幕布局(品牌块 + 社交链接 + 循环渐变背景)以及无缝循环背景模式。
  • references/digit-flip.md — 两个确定性数字动画组件:里程表卷轴(由帧驱动的垂直0–9条带)和翻牌卡(上半部分折叠)效果,以及每个适合用于发布、销售与流场景的说明。

Individual skills in this repo

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

相关技能