mcpField
T
text-animation-copier

text-animation-copier

github.com/andchir创意与媒体447 次安装35 周前更新
代码仓库

关于 text-animation-copier

text-animation-copier 是一项将视频示例中的文字动画重制为 Remotion React 组件的技能,面向视频创作者。它涵盖逐帧分析、动画参数设计、组件生成及在 Remotion Studio 中测试。

主要功能

  1. 01提取并检查视频帧
  2. 02分析文字时序与视觉效果
  3. 03生成 Remotion React 组件
  4. 04在 Remotion Studio 注册并测试合成
  5. 05重制分阶段及逐字动画

适用场景

  1. 01根据视频片段重制标题动画
  2. 02按视频时间码匹配文字运动
  3. 03制作错开或多阶段文字效果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/andchir/remotion-animations --skill text-animation-copier

安装后在 Claude Code 中输入/text-animation-copier确认已加载。

详细介绍

Remotion 文字动画复刻工具

分析视频示例中的文字动画,并将其精确复刻为 Remotion React 组件,包括时间、样式和特效。

快速开始

前置条件

使用此技能前,请确保:

  1. 已安装 ffmpeg - 提取视频帧所需
  2. 已设置 Remotion 项目 - 可正常运行的 remotion-animations 仓库
  3. 视频 URL 或文件路径 - 包含动画视频的直接链接或本地路径
基本工作流

当用户提供包含待复刻文字动画的视频时:

  1. 下载视频并提取帧(使用 claude-skills/video-viewing)
  2. 检查关键帧,分析动画
  3. 识别动画属性(时间、特效、位置、样式)
  4. 生成 Remotion 组件,复刻该动画
  5. 在 Remotion Studio 中测试并优化动画

详细流程

步骤 1:下载视频并提取帧

使用 video-viewing 技能下载视频并提取帧:

python scripts/download_video.py "VIDEO_URL" -f

此操作会将视频下载到 /mnt/user-data/outputs/,并提取帧以供分析。

替代方案: 如果用户提供的是本地视频文件路径,请直接使用 ffmpeg:

# Extract frames at 1 frame per second
ffmpeg -i path/to/video.mp4 -vf fps=1 /tmp/frames/frame-%04d.png

# Extract frames at specific time interval (e.g., 00:07 to 00:13)
ffmpeg -i path/to/video.mp4 -ss 00:07 -to 00:13 -vf fps=2 /tmp/frames/frame-%04d.png
步骤 2:检查帧并分析动画

用户通常会指定一个时间范围(例如“00:07 - 00:13”)。提取并分析该范围内的帧:

  1. 找出动画的起始帧和结束帧
  2. 检查关键转场点(动画行为发生变化的时刻)
  3. 忽略背景视频——只关注文字元素
  4. 记录观察结果,包括:
    • 文字内容和位置
    • 字体、字号、字重、颜色
    • 动画类型(淡入、滑动、缩放、旋转、翻转等)
    • 时间和缓动曲线
    • 多阶段动画(例如,先填充色条,再显示文字)
    • 特殊效果(3D 变换、阴影、辉光、粒子)
步骤 3:识别动画属性

根据帧分析结果,记录以下关键属性:

时间配置
  • 时长:动画总时长,以秒/帧为单位
  • FPS:视频帧率(网络视频通常为 30fps)
  • 关键时刻:重要转场对应的帧编号
  • 缓动:缓动曲线类型(线性、缓入缓出、三次、贝塞尔)

时间结构示例:

const TIMING = {
  PHASE_1_START: 0,
  PHASE_1_END: 45,
  PHASE_2_START: 48,
  PHASE_2_END: 90,
  HOLD_END: 180,
};
文字样式
  • 字体:Arial、Helvetica、自定义字体
  • 字号:像素值
  • 字重:normal、bold、300-900
  • 颜色:Hex 或 rgba 值
  • 字间距:字符之间的额外间距
  • 文字对齐:左对齐、居中、右对齐
位置
  • 对齐方式:居中、左上、左下等
  • 偏移值:距离边缘的像素值
  • 响应式定位:使用视口宽度/高度实现居中
动画效果

常见文字动画模式:

1. 淡入/淡出

const opacity = interpolate(
  frame,
  [START_FRAME, END_FRAME],
  [0, 1],
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  }
);

2. 滑入/滑出

const translateX = interpolate(
  frame,
  [START_FRAME, END_FRAME],
  [-500, 0], // from left to center
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
    easing: Easing.out(Easing.cubic),
  }
);

3. 缩放动画

const scale = interpolate(
  frame,
  [START_FRAME, END_FRAME],
  [0, 1],
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
    easing: Easing.elastic(1),
  }
);

4. 3D 旋转/翻转

const rotateY = interpolate(
  frame,
  [START_FRAME, END_FRAME],
  [0, 360],
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
    easing: Easing.inOut(Easing.ease),
  }
);

// Apply with CSS transform
transform: `rotateY(${rotateY}deg)`,
transformStyle: "preserve-3d",
perspective: "1000px"

5. 色条填充动画

const barProgress = interpolate(
  frame,
  [BAR_START, BAR_END],
  [0, 1],
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
    easing: Easing.bezier(0.25, 0.1, 0.25, 1),
  }
);

// Apply to width
width: `${barProgress * 100}%`

6. 交错动画(用于多个字母/单词)

// Each letter starts with a delay
const letterStart = BASE_START + index * STAGGER_DELAY;
const letterEnd = letterStart + ANIMATION_DURATION;

const letterOpacity = interpolate(
  frame,
  [letterStart, letterEnd],
  [0, 1],
  { extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);

7. 弹簧物理效果

const scale = spring({
  frame: frame - START_FRAME,
  fps: 30,
  config: {
    damping: 10,
    stiffness: 100,
    mass: 0.5,
  },
});
步骤 4:生成 Remotion 组件

按照以下模式,在 src/animations/text-animation-N/ 中新建 React 组件:

组件结构模板
import React from "react";
import {
  AbsoluteFill,
  useCurrentFrame,
  useVideoConfig,
  interpolate,
  Easing,
  spring,
} from "remotion";

// Animation timing configuration (in frames, at 30fps)
// Reference video time range: HH:MM:SS - HH:MM:SS
const TIMING = {
  ANIMATION_START: 0,
  ANIMATION_END: 60,
  // Add more timing points as needed
};

// Configuration constants
const TEXT = "YOUR TEXT HERE";
const FONT_SIZE = 48;
const FONT_FAMILY = "Arial, sans-serif";
const TEXT_COLOR = "#FFFFFF";

export const TextAnimationN: React.FC = () => {
  const frame = useCurrentFrame();
  const { width, height } = useVideoConfig();

  // Calculate animation values based on current frame
  const opacity = interpolate(
    frame,
    [TIMING.ANIMATION_START, TIMING.ANIMATION_END],
    [0, 1],
    {
      extrapolateLeft: "clamp",
      extrapolateRight: "clamp",
      easing: Easing.inOut(Easing.ease),
    }
  );

  return (
    <AbsoluteFill
      style={{
        backgroundColor: "transparent", // Always transparent for overlays
      }}
    >
      {/* Your animation elements here */}
      <div
        style={{
          fontFamily: FONT_FAMILY,
          fontSize: `${FONT_SIZE}px`,
          color: TEXT_COLOR,
          opacity: opacity,
          // Add positioning, transforms, etc.
        }}
      >
        {TEXT}
      </div>
    </AbsoluteFill>
  );
};
重要要求
  1. 分辨率:始终使用 1920x1080(在 Root.tsx 中配置)
  2. 透明背景:backgroundColor: "transparent"
  3. 居中定位:使用 width/2 和 height/2 实现居中
  4. 精确计时:尽可能贴合原视频的时间
  5. 代码清晰:添加注释,说明每个动画阶段
步骤 5:注册合成

将新动画添加到 src/Root.tsx:

import { TextAnimationN } from "./animations/text-animation-N/TextAnimationN";

<Composition
  id="TextAnimationN"
  component={TextAnimationN}
  durationInFrames={180} // Adjust based on animation length
  fps={30}
  width={1920}
  height={1080}
  defaultProps={{}}
/>
第 6 步:在 Remotion Studio 中测试
npm start

这会在 http://localhost:3000 打开 Remotion Studio,你可以:

  • 实时预览动画
  • 拖动时间轴浏览各帧,检查时间设置
  • 与原始视频进行对比
  • 调整数值并立即查看效果
第 7 步:渲染动画

对动画效果满意后:

# Render as WebM with transparency (recommended)
npm run render:text-animation-N

# Or add custom render script to package.json:
npx remotion render src/index.ts TextAnimationN out/text-animation-N.webm --codec vp9 --pixel-format yuva420p

高级技巧

逐字母动画

适用于每个字母独立进行动画的效果:

const Letter: React.FC<{
  char: string;
  index: number;
  totalLetters: number;
}> = ({ char, index, totalLetters }) => {
  const frame = useCurrentFrame();

  // Stagger each letter's animation
  const letterDelay = index * 3; // 3 frames delay per letter
  const letterStart = TIMING.ANIMATION_START + letterDelay;

  const opacity = interpolate(
    frame,
    [letterStart, letterStart + 15],
    [0, 1],
    { extrapolateLeft: "clamp", extrapolateRight: "clamp" }
  );

  return (
    <span style={{ opacity }}>
      {char}
    </span>
  );
};

// In main component:
const letters = TEXT.split("").map((char, i) => (
  <Letter key={i} char={char} index={i} totalLetters={TEXT.length} />
));
多阶段动画

适用于包含多个连续阶段的复杂动画:

// Phase 1: Slide in
const slideProgress = interpolate(
  frame,
  [TIMING.SLIDE_START, TIMING.SLIDE_END],
  [0, 1],
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
    easing: Easing.out(Easing.cubic),
  }
);

// Phase 2: Scale pulse (starts after slide completes)
const scaleProgress = interpolate(
  frame,
  [TIMING.SCALE_START, TIMING.SCALE_END],
  [1, 1.2],
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
    easing: Easing.inOut(Easing.ease),
  }
);

// Phase 3: Fade out
const fadeOutProgress = interpolate(
  frame,
  [TIMING.FADEOUT_START, TIMING.FADEOUT_END],
  [1, 0],
  {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  }
);

// Combine all phases
const translateX = slideProgress * width;
const scale = frame >= TIMING.SCALE_START ? scaleProgress : 1;
const opacity = frame >= TIMING.FADEOUT_START ? fadeOutProgress : 1;
背景效果

适用于带有动画背景的文本(如 TextAnimation2):

// Background appears behind text with 3D rotation
<div
  style={{
    position: "absolute",
    width: charWidth + 20,
    height: fontSize + 20,
    backgroundColor: BACKGROUND_COLOR,
    transform: `rotateY(${bgRotateY}deg) scaleX(${bgScaleX})`,
    transformStyle: "preserve-3d",
    opacity: bgOpacity,
    zIndex: -1, // Behind text
  }}
/>
自定义缓动函数

适用于精确匹配时间的场景:

// Built-in easing options:
Easing.linear           // Constant speed
Easing.ease             // Gentle start and end
Easing.in(Easing.quad)  // Accelerate
Easing.out(Easing.quad) // Decelerate
Easing.inOut(Easing.cubic) // Smooth S-curve
Easing.bezier(0.25, 0.1, 0.25, 1) // Custom cubic bezier

// Available easing functions:
// quad, cubic, poly, sin, exp, circle, back, bounce, elastic

技术参考

Remotion 核心 Hook
  • useCurrentFrame():返回当前帧数
  • useVideoConfig():返回 { fps, width, height, durationInFrames }
  • interpolate(value, inputRange, outputRange, options):平滑映射数值
  • spring(options):基于物理效果的动画
  • Sequence:通过偏移量叠加多个动画
Remotion 必备组件
  • <AbsoluteFill>:全屏定位容器
  • <Sequence from={frame} durationInFrames={frames}>:时间偏移动画
  • <Audio>:背景音乐/音效
  • <Video>:嵌入视频背景
  • <Img>:静态或动态图片
CSS 变换属性

对于 3D 效果,请使用以下变换属性:

perspective: 1000px;              /* Enable 3D space */
transformStyle: preserve-3d;      /* Maintain 3D in children */
backfaceVisibility: hidden;       /* Hide back of element */

/* Transform functions: */
transform: translateX(px) translateY(px) translateZ(px)
          rotateX(deg) rotateY(deg) rotateZ(deg)
          scale(factor) scaleX() scaleY()
          skewX(deg) skewY(deg);

常见动画模式

1. 文字下三分之一(类似 TextAnimation1)
  • 彩色条从左向右填充
  • 彩色条完成后显示文字
  • 放置在角落(通常是左下角)
2. 带 3D 翻转的飞入文字(类似 TextAnimation2)
  • 文字从屏幕外飞入
  • 每个字母在移动时进行 3D 翻转
  • 背景方框旋转到位,显示在字母后方
  • 采用错开的时间安排,增强视觉效果
3. 打字机效果
const visibleChars = Math.floor(
  interpolate(
    frame,
    [START, END],
    [0, TEXT.length],
    { extrapolateRight: "clamp" }
  )
);

return <div>{TEXT.substring(0, visibleChars)}</div>;
4. 弹跳进入
const bounceScale = spring({
  frame: frame - START_FRAME,
  fps: 30,
  config: {
    damping: 12,
    stiffness: 200,
    mass: 0.5,
  },
});
5. 故障效果
const glitchOffset = Math.random() * 10 - 5;
const glitchActive = frame % 10 === 0; // Every 10 frames

return (
  <div style={{
    transform: glitchActive
      ? `translate(${glitchOffset}px, ${glitchOffset}px)`
      : 'none',
  }}>
    {TEXT}
  </div>
);

最佳实践

1. 精确控制时间
  • 将时间转换为帧数:30fps 时,1 秒 = 30 帧
  • 匹配原始时间:使用视频时间码设置 TIMING 常量
  • 考虑延迟:错开动画,营造流畅的视觉节奏
2. 性能
  • 避免逐帧进行复杂计算:尽可能提前计算
  • 优先使用 interpolate,而非手动计算:它经过优化,能够处理边界情况
  • 尽量减少重新渲染:保持组件逻辑简单
3. 代码组织
  • 将常量放在顶部:把 TIMING、COLORS、SIZES 集中在一处
  • 为各阶段添加注释:说明每个动画阶段的作用
  • 使用可复用组件:提取重复模式(如 Letter 组件)
4. 视觉还原度
  • 尽可能匹配字体:使用 Web 安全字体或加载自定义字体
  • 确保颜色准确:使用视频帧中的颜色选取器
  • 精确定位:测量视频帧中的距离
5. 测试工作流
  • 在 Remotion Studio 中测试:立即获得视觉反馈
  • 并排对比:播放原始视频,同时查看动画
  • 检查所有帧:拖动时间轴浏览,确认过渡流畅
  • 渲染并检查:最终渲染效果可能与预览不同

故障排除

动画时间不匹配
  • 确认 FPS 与原始视频一致(通常为 30fps)
  • 检查 TIMING 常量是否准确
  • 使用 console.log(frame) 调试帧数值
文字位置不正确
  • 再次检查 useVideoConfig() 返回的 width 和 height
  • 确认居中计算:width/2 - textWidth/2
  • 在 Remotion Studio 中测试不同的视口尺寸
动画卡顿
  • 增加合成中的 durationInFrames
  • 检查缓动函数是否平滑(而非线性)
  • 确认帧率稳定(30fps)
3D 变换无效
  • 在父元素上添加 perspective: "1000px"
  • 添加 transformStyle: "preserve-3d"
  • 检查浏览器是否支持 3D 变换
文本未显示
  • 确认 opacity 不为 0
  • 检查 zIndex 层叠顺序
  • 确保颜色与背景形成对比
  • 确认帧范围包含文本出现的时间

示例工作流

示例 1:文本淡入

用户提供:文本从 00:03 到 00:05 淡入的视频

  1. 下载视频并提取帧
  2. 分析帧:文本 "HELLO" 的不透明度从 0% 渐变到 100%
  3. 计算帧数:3 秒到 5 秒 = 第 90 帧到第 150 帧(30fps)
  4. 创建带淡入插值的组件
  5. 测试并渲染
示例 2:多行文本交错出现

用户提供:3 行文本从 00:10 到 00:15 依次出现的视频

  1. 提取 10-15 秒的帧
  2. 确定:第 1 行在 10 秒出现,第 2 行在 12 秒出现,第 3 行在 14 秒出现
  3. 计算每行的时间(30fps)
  4. 创建包含 3 个文本元素的组件,并为每个元素设置不同的时间偏移
  5. 测试交错延迟是否与原视频一致
  6. 渲染
示例 3:复杂的多阶段动画

用户提供:文本从 00:20 到 00:30 滑入、旋转,然后滑出的视频

  1. 每隔 0.5 秒提取一帧
  2. 确定各个阶段:
    • 阶段 1(20-23 秒):从左侧滑入
    • 阶段 2(23-26 秒):旋转 360 度
    • 阶段 3(26-30 秒):向右滑出
  3. 为每个阶段创建 TIMING 常量
  4. 实现 3 个独立的插值
  5. 在 style 对象中组合变换
  6. 测试各阶段之间是否平滑过渡
  7. 渲染并比较

与其他技能集成

此技能与以下内容结合使用效果最佳:

  • claude-skills/video-viewing:用于下载视频和提取帧
  • remotion-best-practices:用于掌握高级 Remotion 技术
  • 视频编辑知识:了解时间、缓动和关键帧

输出检查清单

交付文本动画复刻时,请确保:

  • 在 src/animations/text-animation-N/ 中创建组件
  • 在 src/Root.tsx 中注册
  • 分辨率为 1920x1080
  • 背景透明
  • 文本居中(如有指定)
  • 时间与原视频一致
  • 为 TIMING 常量添加注释说明
  • 代码简洁且注释完善
  • 在 Remotion Studio 中测试
  • 在 package.json 中添加渲染脚本(可选)
  • 在注释中注明原视频的时间范围

命令速查

# Download video with frame extraction
python scripts/download_video.py "VIDEO_URL" -f

# Start Remotion Studio for testing
npm start

# Type check the code
npm run typecheck

# Render animation
npx remotion render src/index.ts TextAnimationN out/output.webm --codec vp9 --pixel-format yuva420p

# Extract specific time range from video
ffmpeg -i input.mp4 -ss 00:07 -to 00:13 -vf fps=2 output-%04d.png

实用资源

总结

此技能帮助你系统地分析视频中的文本动画,并将其复刻为可复用的 Remotion 组件。关键在于仔细观察、精确转换时间,并充分利用 Remotion 强大的插值和动画功能。

始终优先匹配原动画的时间和视觉感受,而非追求像素级复刻。缓动或时间上的细微变化可以让动画感觉更自然,同时仍能保留原动画的精髓。

评论

支持 Markdown