Stitch → Remotion 演示视频制作
约束: 仅在用户明确提到“Stitch”以及演示视频、演示或 Remotion 时使用此技能。
你是一位视频制作专家,负责根据 Stitch 应用设计稿制作演示视频。你将获取 Stitch 屏幕截图,并构建一个 Remotion 合成——包含滑动过渡、缩放动画和文字叠加。
前提条件
- Stitch MCP 服务器(或已知的屏幕 ID)
- Node.js 18+ 和 npm
- Remotion CLI(
npm install -g remotion或通过npx使用)
第一步:收集 Stitch 素材
发现屏幕
- 运行
list_tools→ 找到 Stitch MCP 前缀 - 调用
[前缀]:list_projects→ 选择项目 - 调用
[前缀]:list_screens,参数为projects/[项目ID]→ 列出所有屏幕 - 对于视频中需要的每个屏幕,调用
[前缀]:get_screen,参数为数字 ID
下载屏幕截图
针对每个屏幕:
# 将截图下载到 assets 目录
curl -L "[screenshot.downloadUrl]" -o "video/public/assets/[屏幕名称].png"
或使用抓取脚本:
bash scripts/fetch-stitch.sh "[htmlCode.downloadUrl]" "temp/[屏幕名称].html"
# 截图是独立的 — 通过 curl 使用截图 URL 下载
构建屏幕清单
创建 screens.json 描述视频:
{
"projectName": "我的应用",
"fps": 30,
"screens": [
{
"id": "home",
"title": "首页",
"description": "包含关键指标的主仪表盘",
"imagePath": "./public/assets/home.png",
"width": 390,
"height": 844,
"durationSeconds": 4
},
{
"id": "profile",
"title": "用户资料",
"description": "设置和账户管理",
"imagePath": "./public/assets/profile.png",
"width": 390,
"height": 844,
"durationSeconds": 3
}
]
}
第二步:设置 Remotion 项目
# 在工作目录内创建新的 Remotion 项目
cd video
npm create video@latest -- --blank
cd walkthrough-video
npm install @remotion/transitions
第三步:构建合成
ScreenSlide 组件
// video/src/ScreenSlide.tsx
import { AbsoluteFill, Img, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion'
interface ScreenSlideProps {
/** 截图图片路径 */
imagePath: string
/** 作为叠加层显示的屏幕标题 */
title: string
/** 辅助描述文本 */
description: string
/** 显示期间是否略微缩放 */
withZoom?: boolean
}
/**
* 单个屏幕幻灯片,带有可选的缩放效果和文字叠加。
* 淡入、保持、然后淡出。
*/
export function ScreenSlide({ imagePath, title, description, withZoom = true }: ScreenSlideProps) {
const frame = useCurrentFrame()
const { fps, durationInFrames } = useVideoConfig()
// 前15帧淡入
const fadeIn = spring({ fps, frame, config: { damping: 200 } })
// 细微缩放:100% → 105%
const scale = withZoom
? interpolate(frame, [0, durationInFrames], [1, 1.05], { extrapolateRight: 'clamp' })
: 1
return (
<AbsoluteFill style={{ backgroundColor: '#000' }}>
{/* 截图 */}
<AbsoluteFill style={{ opacity: fadeIn, transform: `scale(${scale})`, transformOrigin: 'center' }}>
<Img src={imagePath} style={{ width: '100%', height: '100%', objectFit: 'contain' }} />
</AbsoluteFill>
{/* 底部文字叠加 */}
<AbsoluteFill
style={{
justifyContent: 'flex-end',
padding: '40px 60px',
background: 'linear-gradient(transparent, rgba(0,0,0,0.7))',
opacity: fadeIn,
}}
>
<h2 style={{ color: '#fff', fontSize: 36, fontWeight: 700, margin: 0 }}>{title}</h2>
{description ? (
<p style={{ color: 'rgba(255,255,255,0.8)', fontSize: 20, margin: '8px 0 0' }}>
{description}
</p>
) : null}
</AbsoluteFill>
</AbsoluteFill>
)
}
Walkthrough 合成
// video/src/WalkthroughComposition.tsx
import { Series, TransitionSeries } from '@remotion/transitions'
import { fade } from '@remotion/transitions/fade'
import { slide } from '@remotion/transitions/slide'
import screensData from '../../screens.json'
import { ScreenSlide } from './ScreenSlide'
const TRANSITION_FRAMES = 15 // 30fps 下 0.5 秒
/**
* 主演示合成 —— 每个屏幕一个幻灯片,带淡入/滑动过渡。
*/
export function WalkthroughComposition() {
return (
<TransitionSeries>
{screensData.screens.map((screen, i) => (
<>
<TransitionSeries.Sequence
key={screen.id}
durationInFrames={screen.durationSeconds * screensData.fps}
>
<ScreenSlide
imagePath={screen.imagePath}
title={screen.title}
description={screen.description}
/>
</TransitionSeries.Sequence>
{/* 屏幕之间添加过渡(最后一个之后不添加) */}
{i < screensData.screens.length - 1 && (
<TransitionSeries.Transition
key={`t-${screen.id}`}
timing={fade({ durationInFrames: TRANSITION_FRAMES })}
/>
)}
</>
))}
</TransitionSeries>
)
}
在 Root.tsx 中注册
// video/src/Root.tsx
import { Composition } from 'remotion'
import { WalkthroughComposition } from './WalkthroughComposition'
import screensData from '../../screens.json'
const TOTAL_FRAMES = screensData.screens.reduce(
(acc, s) => acc + s.durationSeconds * screensData.fps,
0
)
export const RemotionRoot = () => (
<>
<Composition
id="Walkthrough"
component={WalkthroughComposition}
durationInFrames={TOTAL_FRAMES}
fps={screensData.fps}
width={screensData.screens[0]?.width ?? 390}
height={screensData.screens[0]?.height ?? 844}
/>
</>
)
第四步:预览和渲染
# 在浏览器中预览
cd video/walkthrough-video
npm run dev
# 渲染为 MP4
npx remotion render Walkthrough output.mp4
# 更高品质渲染
npx remotion render Walkthrough output.mp4 --jpeg-quality 95 --concurrency 4
常见视频风格
| 风格 | 配置 |
|---|---|
| 快速演示(1–2 分钟) | 每个屏幕 2–3 秒,淡入过渡,仅标题 |
| 功能讲解 | 每个屏幕 4–5 秒,缩放+滑动,标题+描述 |
| 演示文稿 | 每个屏幕 5–8 秒,仅淡入,完整描述叠加 |
| 社交媒体短视频 | 每个屏幕 1–2 秒,快速切换,背景音乐 |
文件结构
project/
├── screens.json ← 屏幕清单
├── scripts/fetch-stitch.sh ← GCS 下载器
└── video/
├── public/
│ └── assets/ ← 下载的截图
└── walkthrough-video/
├── src/
│ ├── WalkthroughComposition.tsx
│ ├── ScreenSlide.tsx
│ └── Root.tsx
├── remotion.config.ts
└── package.json
故障排除
| 问题 | 解决方法 |
|---|---|
| 截图模糊 | 下载全分辨率:直接使用截图 URL,而非缩略图 |
| 布局不匹配 | 将 Remotion 合成尺寸设置为与 get_screen 中的屏幕 width + height 一致 |
| 过渡卡顿 | 增加 TRANSITION_FRAMES 或从 slide 切换到 fade |
| 构建因 ESM 失败 | 在 package.json 中添加 "type": "module",并检查 Remotion 版本兼容性 |
| MP4 无法播放 | 检查是否安装了 FFmpeg:ffmpeg -version |
参考资料
- Remotion 文档:https://www.remotion.dev/docs/
- Remotion 过渡:https://www.remotion.dev/docs/transitions
scripts/fetch-stitch.sh— 可靠的 GCS 下载器stitch-mcp-list-screens— 发现 Stitch 项目中的屏幕stitch-mcp-get-screen— 获取截图下载 URL