| name | svg-animator |
| description | Generate animated videos from SVG frames using text LLM. Supports any subject (animals, humans, characters, scenes, abstract art), automatic duration calculation, and multi-scene story animations. Perfect替代 AI 视频生成 API,直接用文本模型生成 SVG 代码然后合成视频。 |
SVG Animator
用文本模型生成 SVG 代码,逐帧渲染后合成视频。完全不需要视频生成 API!
支持的场景
- 动物:小狗奔跑、小猫跳跃、鱼儿游泳、鸟儿飞翔
- 人物:走路、跑步、跳舞、工作场景
- 场景:日出日落、云彩飘动、树叶落下、雨滴下落
- 抽象:几何变换、颜色渐变、粒子效果
- 故事:多场景连续动画(需要多段拼接)
使用流程
用户:生成一个"小猫伸懒腰"的动画
↓
1. 理解需求 → 确定主题、动作、时长
2. 设计帧序列 → 每帧的 SVG 代码
3. 生成帧 → Node.js 生成 SVG → rsvg-convert 转 PNG
4. 合成视频 → ffmpeg 合成 MP4/GIF
5. 输出 → 返回文件路径或 nginx 链接
时长计算规则
| 需求 | 帧数 | 时长 | fps |
|---|
| 简单动作(伸懒腰、眨眼) | 8-12帧 | 1秒 | 10 |
| 常规动作(走路、跑步) | 16-24帧 | 2秒 | 12 |
| 复杂动作(跳舞、翻滚) | 30-48帧 | 3-4秒 | 12 |
| 短故事(2-3个场景) | 60-120帧 | 5-10秒 | 12 |
| 长故事(5+场景) | 120+帧 | 10秒+ | 15 |
动画原理
周期性运动(正弦函数)
const t = (i / totalFrames) * Math.PI * 2;
const legOffset = Math.sin(t) * 40;
const tailAngle = Math.sin(t * 2) * 25;
const bodyY = Math.sin(t) * 5;
多元素协调
const legPhase = t;
const tailPhase = t * 2;
const bodyPhase = t;
legOffset = Math.sin(legPhase) * 40;
tailAngle = Math.sin(tailPhase) * 25;
bodyY = Math.sin(bodyPhase) * 5;
帧生成代码模板
const fs = require('fs');
const { execSync } = require('child_process');
const frames = 24;
const size = 400;
for (let i = 0; i < frames; i++) {
const t = (i / frames) * Math.PI * 2;
const legOffset = Math.sin(t) * 40;
const tailAngle = Math.sin(t * 2) * 25;
const svg = `<svg width='${size}' height='${size}' xmlns='http://www.w3.org/2000/svg'>
<!-- 背景 -->
<rect width='${size}' height='${size}' fill='#87CEEB'/>
<!-- 地面 -->
<rect x='0' y='${size*0.88}' width='${size}' height='${size*0.12}' fill='#90EE90'/>
<!-- 主体 - 使用运动参数 -->
<ellipse cx='${200 + legOffset * 0.5}' cy='${250 + Math.sin(t)*5}' ... />
<!-- 更多元素 -->
</svg>`;
fs.writeFileSync(`/tmp/frames/frame_${String(i).padStart(, )}.svg`, svg);
}
();
();
常用动画参数
走路/奔跑
const runCycle = {
legSwing: Math.sin(t) * 40,
bodyBob: Math.sin(t * 2) * 5,
tailWag: Math.sin(t * 2) * 30,
armSwing: Math.sin(t) * 25
};
飞行
const flyCycle = {
wingUp: Math.sin(t) * -45,
wingDown: Math.sin(t) * 45,
bodyTilt: Math.sin(t) * 10,
glide: Math.sin(t) * 20
};
跳跃
const jumpCycle = {
yOffset: -Math.abs(Math.sin(t)) * 100,
legTuck: Math.abs(Math.sin(t)) * 30,
armUp: Math.abs(Math.sin(t)) * -30
};
摇摆/晃动
const swayCycle = {
rotation: Math.sin(t) * 15,
xOffset: Math.sin(t) * 10,
wave: Math.sin(t * 3) * 5
};
场景动画示例
日出
const progress = i / frames;
const sunY = size * 0.8 - progress * size * 0.6;
const skyGrad = `rgb(${255 * progress}, ${200 * progress}, ${100 + 155 * progress})`;
云飘动
const cloudX = (i / frames) * size * 1.5;
const opacity = 1 - (cloudX / size) * 0.5;
雨滴下落
for (let drop = 0; drop < 10; drop++) {
const startFrame = (frames / 10) * drop;
const dropPos = ((i - startFrame) / frames) * size;
}
多场景故事动画
当用户需要"讲一个故事"时,生成多个场景然后拼接:
const scenes = ['scene1.mp4', 'scene2.mp4', 'scene3.mp4'];
const concatList = scenes.map(s => `file '${s}'`).join('\n');
fs.writeFileSync('/tmp/concat.txt', concatList);
execSync(`ffmpeg -y -f concat -safe 0 -i /tmp/concat.txt -c copy story.mp4`);
完整工作流
Step 1: 分析需求
"生成一个小狗追蝴蝶的故事动画"
分解:
- 主题:小狗 + 蝴蝶
- 动作:追逐(小狗跑 + 蝴蝶飞)
- 场景:草地、天空
- 时长:5-8秒(3个场景)
Step 2: 设计场景
-
场景1(0-3秒):小狗看到蝴蝶
-
场景2(3-6秒):追逐
-
场景3(6-8秒):抓到/没抓到
Step 3: 生成代码
mkdir -p /tmp/animation_frames
node generate_story.js
rsvg-convert /tmp/animation_frames/*.svg -o /tmp/animation_frames/frame_%04d.png
ffmpeg -y -framerate 12 -i /tmp/animation_frames/frame_%04d.png \
-c:v mpeg4 -q:v 2 /tmp/story.mp4
Step 4: 提供下载
复制到 nginx 目录或直接发送文件路径
注意事项
- ffmpeg 必须安装 -
dnf install ffmpeg 或 apt install ffmpeg
- rsvg-convert 必须 - 用于 SVG → PNG
- 帧数不要太多 - 超过 200 帧可能很慢
- 复杂故事分场景 - 不要把所有动作放一帧里
- 背景简洁 - 复杂背景会影响性能
辅助脚本
使用 scripts/animate.js 简化流程:
node scripts/animate.js --theme "dog running" --frames 24 --output /tmp/dog.mp4
node scripts/animate.js --theme "cat stretching" --duration 2 --output /tmp/cat.mp4
node scripts/animate.js --story "小兔子的一天" --scenes 5 --output /tmp/rabbit_day.mp4
输出格式
- MP4:适合分享,文件小
- GIF:适合嵌入网页/文档
- 链接:部署到 nginx 通过 http 提供
ffmpeg -i input.mp4 -vf "fps=12,scale=480:-1:flags=lanczos" output.gif