关于 photo-slideshow
photo-slideshow 是将照片文件夹制作成 MP4 的技能,面向需要回忆集锦或旅行蒙太奇的人。它强调按照片确定性地变化 Ken Burns 运镜、处理混合宽高比,并让切换与音乐节拍同步。
主要功能
- 01为每张照片生成不同的 Ken Burns 运镜
- 02确定性动画便于重复渲染
- 03用转场连接照片
- 04按音乐节拍安排切换
- 05以模糊填充处理不同宽高比
- 06从文件夹清单生成视频
适用场景
- 01制作旅行或婚礼回忆视频
- 02将年度照片整理成配乐集锦
- 03为竖图和横图混合的照片集生成视频
- 04用同一模板批量渲染多个照片文件夹
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ iart-ai/ ecommerce-video-skills --skill photo-slideshow安装后在 Claude Code 中输入/photo-slideshow确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdreferences/aspect-and-layout.mdken-burns.mdtemplating-folder.mdtiming-and-audio.md详细介绍
幻灯片视频
将一组照片制作成一段精美成片,让人感觉像是实地拍摄,而非简单拼接。关键在于为每张静态照片赋予柔和的动态效果(Ken Burns),按照音乐切换幻灯片,优雅地处理不同尺寸的照片,并读取整个文件夹,让同一模板可以渲染任意一组图片。
适用场景
- 一个照片文件夹 → 一段完整的 MP4(核心使用场景)。
- 配乐回忆录、总结、年度回顾、婚礼/旅行蒙太奇。
- 任何需要动态效果、转场和同步节奏的静态图片集。
让幻灯片看起来专业的关键规则
任何两张幻灯片的移动方式都不能相同。业余幻灯片最容易露馅的地方,就是每张照片都做一模一样的缓慢放大。为每张照片改变移动方向、放大或缩小,以及起始缩放比例,但要让这些变化具有确定性(由索引作为种子),这样重新渲染的结果就会一致,基于帧的渲染器也能保持稳定。
// seeded PRNG → same photo always gets the same motion, but each photo differs
const mulberry32 = (a) => () => {
a |= 0; a = (a + 0x6d2b79f5) | 0;
let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
每张照片的 Ken Burns 效果
每张幻灯片从一组精简的运动方式中选择一种,让整组照片富有节奏感而不重复。将 scale 和 translate 一起动画处理;绝不要为 width/height 添加动画(这会触发布局重排并造成闪烁)。
| 参数 | 范围 | 原因 |
|---|---|---|
| 缩放 | 放大 1.0→1.12 或缩小 1.12→1.0 | 效果微妙;超过约 1.15 就会像快速变焦 |
| 平移 | 左/右/上/下/对角线之一 | 方向由种子决定,不能每张都相同 |
| 缓动 | easeInOutSine | 缓慢、连续——Ken Burns 效果不会突然加速 |
| 锚点 | 将 transform-origin 朝向主体 | 平移时让人脸/地平线留在画面内 |
import { useCurrentFrame, interpolate, Easing } from "remotion";
const frame = useCurrentFrame(); // the ONLY clock
const p = interpolate(frame, [0, durInFrames], [0, 1], {
extrapolateRight: "clamp", easing: Easing.inOut(Easing.sin),
});
const scale = startScale + (endScale - startScale) * p; // e.g. 1.0 → 1.10
const x = panX * p, y = panY * p; // px, sign from seed
// style: transform: `scale(${scale}) translate(${x}px, ${y}px)`
起始缩放比例始终要 ≥ 1.05,以免平移时露出空白边缘。完整的、带有主体感知锚点的种子化 <KenBurnsImage> 组件见 references/ken-burns.md。
照片之间的转场
选用一种转场并保持一致——混用擦除、旋转和立方体转场会显得像“套用模板”。0.4–0.6 秒的交叉淡化是稳妥、经典的默认选择;柔和推进适合旅行片段。让前后幻灯片重叠,避免硬切,除非是在节拍点切换。
import { TransitionSeries, linearTiming } from "@remotion/transitions";
import { fade } from "@remotion/transitions/fade";
// <TransitionSeries.Sequence durationInFrames={hold}> ...slide... </Sequence>
// <TransitionSeries.Transition timing={linearTiming({durationInFrames: 15})} presentation={fade()} />
与节拍/音乐同步切换幻灯片
幻灯片的成败取决于节奏。先确定音乐,再获取节拍时间戳,然后让每次幻灯片切换都对齐节拍。离线检测一次节拍,并将其写入 props——绝不要逐帧分析音频。
# extract beat onset times (seconds) with librosa, write to beats.json
python3 -c "import librosa,json,sys; y,sr=librosa.load(sys.argv[1]); \
_,b=librosa.beat.beat_track(y=y,sr=sr); \
json.dump([float(t) for t in librosa.frames_to_time(b,sr=sr)], open('beats.json','w'))" song.mp3
接着将节拍秒数转换为帧数,并分配幻灯片边界。每张照片保持一个音乐单位(通常每隔 2 或 4 拍切换一次——每拍都切换对照片来说太急促)。有关计数到帧数的计算、节奏到幻灯片的分配,以及无需音频分析的备用方案(固定 BPM 网格),请参阅 references/timing-and-audio.md。
不同比例的照片
真实的照片文件夹里,既有手机竖拍照片,也有相机横拍照片。绝不要拉伸——变形不可接受。以下三种策略按优先顺序排列:
| 策略 | 效果 | 适用场景 |
|---|---|---|
| 模糊填充 | 照片适配画框,缩放并模糊后的副本填满空白区域 | 方向混杂,默认选项 |
| 填充(cover) | 照片填满画框,裁掉边缘 | 照片比例与画框大致一致 |
| 适配(letterbox) | 照片完整显示在纯色/渐变背景上 | 每个像素都很重要(文档、艺术作品) |
模糊填充是现代标准,因为它能填满画框,同时不裁掉主体。将同一源图渲染两次:在后方放置 cover 模糊层,前方叠加 contain 清晰照片。组件和方向检测逻辑见 references/aspect-and-layout.md。
字幕、日期、片头/片尾
- 字幕/日期:从文件名、EXIF 或旁置 JSON 文件中读取;将其放在固定的画面下方区域,幻灯片稳定后淡入(约 0.3 秒),离场前淡出。
- 片头卡片:标题 + 日期范围,在第一张照片出现前显示 1.5–2 秒。
- 片尾卡片:结束语或联系信息/行动号召,显示 2–3 秒,让视频以明确的意图结束,而不是在照片播放到一半时渐隐至黑屏。
将文件夹模板化 → 视频
最终效果:将合成指向一个文件夹,根据文件列表生成时间线,然后渲染一次。照片相关内容都不是硬编码的——数量、顺序、字幕和时长都来自数据。
// data is a prop: [{ src, caption?, date? }]; the timeline is computed, not authored
export const Slideshow = ({ photos, beats, music }) => { /* maps photos → slides */ };
# build the manifest from a folder (sorted, with EXIF dates), then render
node scripts/build-manifest.mjs ./photos > photos.json
npx remotion render Slideshow out/slideshow.mp4 --props=photos.json
将颜色、字体、转场和幻灯片时长都放在一个主题对象中,这样每次渲染都保持一致,只需更换照片即可。请参阅 references/templating-folder.md,了解清单生成器(排序、EXIF 日期、去重)、完整的数据驱动 <Slideshow>,以及在无法使用 React 时仅用 FFmpeg 的方案。
输出检查清单
- 每张照片的动效都以索引作为种子——方向和缩放各不相同,不会有两张完全相同。
- 所有动效都是
useCurrentFrame()的纯函数;起始缩放比例 ≥ 1.05。 - 使用统一的转场(默认交叉淡化),采用重叠转场而非硬切。
- 幻灯片切换与节拍对齐;照片至少停留 ≥ 1 个音乐单位,而不是每拍都切换。
- 混合宽高比通过模糊填充处理——不拉伸任何内容。
- 字幕/日期位于固定区域;片头和片尾卡片分别置于照片集的开头和结尾。
- 照片、字幕和时长都来自文件夹清单——一个模板适用于任意照片集。
交付与验证(渲染静帧 → 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。
这是重型任务:交付物是 MP4,而不是实时页面。幻灯片会载入整个文件夹中的用户照片——验证时主要要检查的是每张照片是否都实际加载,并且经过重新构图后没有失真。
输出要求:
- 一个注册了合成的 Remotion 项目(
<Composition>+ zodschema+defaultProps);Ken Burns 缩放/平移、转场和节拍时间安排都必须是useCurrentFrame()的纯函数(不能使用 CSS 转场、计时器、Date.now();动效以索引而非Math.random()作为种子)。 - 通过
staticFile()加载照片,并使用delayRender/continueRender控制渲染,确保每张图片(以及任何字幕字体)都已就绪后才渲染对应帧——否则幻灯片会空白显示。时长取决于数据 → 根据照片数量在calculateMetadata中计算,而不是手动指定。 - 交付物 = 渲染后的
out/*.mp4和项目(文件夹内容变更时重新渲染)。
验证流程——渲染静帧 → 检查 → 编码。 先生成成本较低的 PNG,确认照片效果合适后再渲染视频。
# Frame-exact stills WITH THE MANIFEST YOU'LL SHIP (real photo paths), not defaultProps
npx remotion still Slideshow out/f-intro.png --frame=15 --props=photos.json # intro card
npx remotion still Slideshow out/f-mid.png --frame=200 --props=photos.json # a mid slide mid-Ken-Burns
npx remotion still Slideshow out/f-outro.png --frame=500 --props=photos.json # outro card
# end frame = durationInFrames - 1 (npx remotion compositions reads it)
检查每张 PNG 的保真度(每张幻灯片上的照片是否正确;字幕/日期文本是否正确;模糊填充是否能填充混合宽高比且不拉伸),以及瑕疵(图像空白/未加载、照片拉伸/失真、平移时露出空白边缘——起始缩放比例 ≥ 1.05、字幕超出下三分之一安全区域、宽高比错误/出现黑边)。
# Only after stills are clean:
npx remotion render Slideshow out/slideshow.mp4 --props=photos.json
npx remotion render Slideshow out/slideshow.gif --props=photos.json --codec=gif # README first-screen proof
**批量处理(一个模板,多个文件夹):**使用同一模板处理多组照片时,先通过静帧验证一个代表性文件夹的清单,再批量渲染其余文件夹——一次性发现空白图像或宽高比拉伸问题,避免重复出现 N 次。
完成前:
- 片头/中间/片尾的静帧都能干净渲染——没有错误,照片确实已加载(非空白)。
- 字幕/日期正确,且位于固定的下三分之一安全区域;混合宽高比均以模糊填充处理,没有拉伸;平移时不露出空白边缘。
- 所有动效都由帧驱动,并以索引作为种子——不使用 CSS 转场/计时器/
Date.now()/Math.random()。 - 实际交付的文件夹清单能够正确渲染,而不仅是
defaultProps。 - 完整 MP4 已编码且能正常播放;(可选)为 README 渲染 GIF。
参考文件
references/ken-burns.md— 完整的种子化<KenBurnsImage>Remotion 组件:通过mulberry32(index)为每张照片选择动态效果,基于主体的transform-origin、安全的起始缩放计算,以及对应的 FFmpegzoompan实现。references/timing-and-audio.md— 使用 librosa 检测节拍、将节拍秒数转换为帧、按音乐单位分配幻灯片、无音频分析时的 BPM 网格回退方案,以及每张照片的节奏预算。references/aspect-and-layout.md— 方向检测、模糊填充/裁切铺满/完整显示布局组件,以及适用于 16:9、9:16 和 1:1 的安全区域映射,让一个主版本适配所有平台。references/templating-folder.md— 文件夹→清单构建器(自然排序、EXIF 拍摄日期、从文件名生成字幕)、包含片头/片尾卡片的完整数据驱动<Slideshow>,以及纯 FFmpeg 幻灯片工作流。