소스 정보
- 저장소
- MatrixReligio/ProductVideoCreator
- 최근 소스 활동
- 2026년 1월 26일 05:20
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 41
- 포크
- 8
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/MatrixReligio/ProductVideoCreator --skill compositing명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | compositing |
| description | 使用 Remotion 合成最终视频。当需要将片头、录屏、配音、片尾组合成完整视频时使用。包含动画效果、时间线管理、多尺寸模板和故障处理。 |
项目提供了可复用的配置和组件模板,位于 templates/ 目录:
templates/
├── config/ # 配置模板
│ ├── scenes.ts # 场景时间配置
│ ├── theme.ts # 主题颜色配置
│ ├── types.ts # TypeScript 类型定义
│ ├── videoPresets.ts # 多尺寸视频预设
│ └── index.ts # 统一导出
└── components/ # 组件模板
├── SubtitleDisplay.tsx # 字幕组件
├── AnimatedText.tsx # 动画文字组件
├── BackgroundEffects.tsx # 背景效果组件
├── BrandElements.tsx # 品牌元素组件
├── useResponsive.ts # 响应式 Hook
└── index.ts # 统一导出
cp -r templates/config src/config
cp -r templates/components src/components
根据项目需要修改配置(颜色、场景时间等)
在组件中导入使用:
import { SCENES, FPS, VIDEO_DURATION } from "./config";
import { THEME, getGlowStyle } from "./config";
import { SubtitleDisplay, FadeInText, ParticleField } from "./components";
| 名称 | 分辨率 | 比例 | 适用平台 |
|---|---|---|---|
| 1080p (默认) | 1920×1080 | 16:9 | YouTube, 官网 |
| 720p | 1280×720 | 16:9 | 快速预览, 低带宽 |
| vertical | 1080×1920 | 9:16 | 抖音, 小红书, Reels |
| square | 1080×1080 | 1:1 | Instagram, 微信 |
| 4K | 3840×2160 | 16:9 | 高端展示 |
// videoPresets.ts
export const VIDEO_PRESETS = {
"1080p": { width: 1920, height: 1080, name: "Full HD" },
"720p": { width: 1280, height: 720, name: "HD" },
"vertical": { width: 1080, height: 1920, name: "Vertical" },
"square": { width: 1080, height: 1080, name: "Square" },
"4k": { width: 3840, height: 2160, name: "4K" },
} as const;
export type VideoPreset = keyof typeof VIDEO_PRESETS;
import { Composition } from "remotion";
import { MainVideo } from "./MainVideo";
import { VIDEO_PRESETS, VideoPreset } from "./videoPresets";
const FPS = 30;
const DURATION_SECONDS = 85;
export const RemotionRoot: React.FC = () => {
return (
<>
{/* 为每个尺寸创建 Composition */}
{Object.entries(VIDEO_PRESETS).map(([key, preset]) => (
<Composition
key={key}
id={`Video-${key}`}
component={MainVideo}
durationInFrames={DURATION_SECONDS * FPS}
fps={FPS}
width={preset.width}
height={preset.height}
defaultProps={{ preset: key as VideoPreset }}
/>
))}
</>
);
};
// 根据视频尺寸调整布局
const ResponsiveLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { width, height } = useVideoConfig();
const aspectRatio = width / height;
// 竖屏布局 (9:16)
if (aspectRatio < 1) {
return (
<AbsoluteFill style={{ flexDirection: "column", padding: "60px 40px" }}>
{children}
</AbsoluteFill>
);
}
// 方形布局 (1:1)
if (aspectRatio === 1) {
return (
<AbsoluteFill style={{ padding: "40px" }}>
{children}
</AbsoluteFill>
);
}
// 横屏布局 (16:9)
return (
<AbsoluteFill style={{ padding: "60px 120px" }}>
{children}
</AbsoluteFill>
);
};
// 根据分辨率计算字体大小
const getResponsiveFontSize = (baseSize: number): number => {
const { width, height } = useVideoConfig();
const scale = Math.min(width / 1920, height / 1080);
return Math.round(baseSize * scale);
};
// 使用示例
const title = getResponsiveFontSize(72); // 1080p 下 72px
#!/bin/bash
# render_all_sizes.sh
SIZES=("1080p" "720p" "vertical" "square")
OUTPUT_DIR="out"
for size in "${SIZES[@]}"; do
echo "渲染 $size..."
npx remotion render src/index.ts "Video-$size" "$OUTPUT_DIR/video_$size.mp4"
done
echo "所有尺寸渲染完成!"
Remotion 是 React-based 的视频渲染框架,使用 React 组件定义视频内容。
| 概念 | 说明 |
|---|---|
| Composition | 视频组合定义(分辨率、帧率、时长) |
| Sequence | 时间序列,控制内容出现时机 |
| useCurrentFrame | 获取当前帧数 |
| interpolate | 数值插值,用于动画 |
| spring | 弹性动画 |
import { Composition } from "remotion";
export const RemotionRoot = () => {
return (
<Composition
id="FinalVideo"
component={FinalVideo}
durationInFrames={2550} // 85秒 * 30fps
fps={30}
width={1920}
height={1080}
/>
);
};
问题表现:
Error: Tried to download file xxx, but the server sent no data for 20 seconds
解决方案 A: 手动下载 Chrome Headless Shell
# 1. 手动下载 (更长超时)
curl -L --connect-timeout 30 --max-time 300 \
"https://storage.googleapis.com/chrome-for-testing-public/134.0.6998.35/mac-arm64/chrome-headless-shell-mac-arm64.zip" \
-o /tmp/chrome-headless-shell.zip
# 2. 解压到 Remotion 缓存目录
mkdir -p ~/.cache/remotion
unzip /tmp/chrome-headless-shell.zip -d ~/.cache/remotion/
# 3. 渲染时指定浏览器路径
npx remotion render src/index.ts VideoId out/video.mp4 \
--browser-executable="$HOME/.cache/remotion/chrome-headless-shell-mac-arm64/chrome-headless-shell"
解决方案 B: 使用代理
export HTTP_PROXY=http://your-proxy:port
export HTTPS_PROXY=http://your-proxy:port
npx remotion browser ensure
| 问题 | 解决方案 |
|---|---|
| Chrome 下载失败 | 手动下载或使用代理 |
| 视频文件找不到 | 确保在 public/ 目录,用 staticFile() |
| 渲染内存不足 | --concurrency=4 减少并发 |
| 字体不显示 | 使用 @remotion/google-fonts |
npm install @remotion/google-fonts
// 加载中文字体
import { loadFont } from "@remotion/google-fonts/NotoSansSC";
const { fontFamily } = loadFont();
// 在组件中使用
<div style={{ fontFamily }}>中文文字</div>
| 字体 | 包名 | 风格 |
|---|---|---|
| Noto Sans SC | NotoSansSC | 现代无衬线 |
| Noto Serif SC | NotoSerifSC | 经典衬线 |
| ZCOOL XiaoWei | ZCOOLXiaoWei | 手写风格 |
const fontFamily = `
"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
"WenQuanYi Micro Hei", system-ui, sans-serif
`;
const LogoWithGlow: React.FC<{ src: string }> = ({ src }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
frame,
fps,
config: { damping: 12, stiffness: 100 },
});
const glowIntensity = interpolate(
Math.sin(frame * 0.08),
[-1, 1],
[0.3, 0.8]
);
return (
<div
style={{
transform: `scale(${scale})`,
filter: `drop-shadow(0 0 ${40 * glowIntensity}px #76B900)`,
}}
>
<Img src={src} style={{ width: 400 }} />
</div>
);
};
const FadeInText: React.FC<{ text: string; delay?: number }> = ({
text,
delay = 0,
}) => {
const frame = useCurrentFrame();
const opacity = interpolate(frame, [delay, delay + 30], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const translateY = interpolate(frame, [delay, delay + 30], [20, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<div
style={{
opacity,
transform: `translateY(${translateY}px)`,
}}
>
{text}
</div>
);
};
const TypewriterText: React.FC<{ text: string; speed?: number }> = ({
text,
speed = 3,
}) => {
const frame = useCurrentFrame();
const charsToShow = Math.floor(
interpolate(frame, [0, text.length * speed], [0, text.length], {
extrapolateRight: "clamp",
})
);
return <span>{text.slice(0, charsToShow)}</span>;
};
const YearDisplay: React.FC<{ year: string; color?: string }> = ({
year,
color = "#76B900",
}) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
frame,
fps,
config: { damping: 15, stiffness: 80 },
});
return (
<div
style={{
fontSize: 200,
fontWeight: "bold",
color,
transform: `scale(${scale})`,
textShadow: `0 0 60px ${color}`,
}}
>
{year}
</div>
);
};
const BackgroundWithOverlay: React.FC<{
src: string;
opacity?: number;
}> = ({ src, opacity = 0.3 }) => {
return (
<>
<AbsoluteFill>
<Img
src={staticFile(src)}
style={{
width: "100%",
height: "100%",
objectFit: "cover",
opacity,
}}
/>
</AbsoluteFill>
{/* 渐变叠加层 */}
<AbsoluteFill
style={{
background: "linear-gradient(180deg, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.3) 50%, rgba(0,0,0,0.8) 100%)",
}}
/>
</>
);
};
const DataCard: React.FC<{
value: string;
label: string;
delay: number;