بنقرة واحدة
fkbuilder
fkbuilder 视频构建技能。当用户说"创建视频"、"构建视频"、"使用 fkbuilder"、"视频配置"时调用此技能。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
fkbuilder 视频构建技能。当用户说"创建视频"、"构建视频"、"使用 fkbuilder"、"视频配置"时调用此技能。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
| name | fkbuilder |
| description | fkbuilder 视频构建技能。当用户说"创建视频"、"构建视频"、"使用 fkbuilder"、"视频配置"时调用此技能。 |
| allowed-tools | Read, Write, Edit, Glob, Grep, Bash |
| license | MIT |
fkbuilder - Node.js 程序化视频创作库,通过代码 API 创建视频。
npm install fkbuilder
import { VideoBuilder } from 'fkbuilder';
const builder = new VideoBuilder({
width: 1280,
height: 720,
fps: 30,
});
const track = builder.createTrack({ zIndex: 1 });
const scene = track.createScene({ duration: 3, startTime: 0 });
scene.addBackground({ color: '#0f0f23' });
scene.addText({
text: 'Hello World',
x: '50%', y: '50%',
fontSize: 72, fontFamily: 'Arial',
color: '#ffffff', textAlign: 'center', textBaseline: 'middle',
duration: 3,
});
const outputPath = './output/video.mp4';
await builder.render(outputPath);
const builder = new VideoBuilder({
width: 1920, // 视频宽度,默认 1920
height: 1080, // 视频高度,默认 1080
fps: 30, // 帧率,默认 30
});
| 方法 | 说明 |
|---|---|
createTrack(config) | 创建轨道 |
render(outputPath, options) | 渲染并导出视频 |
initialize() | 预加载异步资源(SVG等) |
destroy() | 销毁构建器 |
await builder.render(outputPath, {
parallel: false, // 是否并行渲染,默认 true
usePipe: true, // 使用管道模式(不保存中间帧),默认 true
});
const track = builder.createTrack({
zIndex: 1, // 层级顺序,数字越大越上层
name: 'main', // 轨道名称(可选)
});
| 方法 | 说明 |
|---|---|
createScene(config) | 创建场景 |
addTransition(config) | 添加转场 |
getTotalDuration() | 获取总时长 |
const scene = track.createScene({
duration: 3, // 持续时间(秒)
startTime: 0, // 开始时间(秒),可选
});
| 方法 | 说明 |
|---|---|
addBackground(config) | 添加背景 |
addText(config) | 添加文本 |
addRect(config) | 添加矩形 |
addCircle(config) | 添加圆形 |
addImage(config) | 添加图片 |
addVideo(config) | 添加视频 |
addSVG(config) | 添加 SVG |
addAudio(config) | 添加音频 |
addSubtitles(config) | 添加字幕 |
scene.addBackground({ color: '#0f0f23' });
// 或
scene.addBackground({ image: './bg.png' });
所有元素都支持以下属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
x | number/string | 0 | X坐标,支持 "50%" 百分比 |
y | number/string | 0 | Y坐标,支持 "50%" 百分比 |
width | number | - | 宽度 |
height | number | - | 高度 |
opacity | number | 1 | 透明度 (0-1) |
rotation | number | 0 | 旋转角度(度) |
scaleX | number | 1 | X方向缩放 |
scaleY | number | 1 | Y方向缩放 |
anchor | array | [0.5, 0.5] | 锚点 [x, y],0-1 |
duration | number | - | 持续时间(秒) |
startTime | number | 0 | 开始时间(秒),相对于场景 |
animations | array | [] | 预设动画数组 |
onFrame | function | - | 帧回调 |
scene.addText({
text: 'FOLIKO',
x: '50%', y: '40%',
fontSize: 120,
fontFamily: 'Arial Black',
fontWeight: 'bold',
fontStyle: 'normal',
color: '#ffffff',
textAlign: 'center', // left/center/right
textBaseline: 'middle', // top/middle/bottom
lineHeight: 1.2,
duration: 3,
startTime: 0,
animations: ['zoomIn'],
// 文字阴影
textShadow: true,
textShadowColor: '#6366f1',
textShadowBlur: 30,
textShadowOffsetX: 2,
textShadowOffsetY: 2,
// 文字描边
stroke: true,
strokeColor: '#000000',
strokeWidth: 2,
// 渐变
gradient: true,
gradientColors: ['#FF6B6B', '#4ECDC4'],
gradientDirection: 'horizontal',
// 拆分动画
split: 'letter', // letter/word/line
splitDelay: 0.1,
splitDuration: 0.3,
});
scene.addRect({
x: '50%', y: '50%',
width: 600,
height: 60,
bgcolor: '#1e1b4b',
borderRadius: 10,
borderWidth: 2,
borderColor: '#22c55e',
shadowBlur: 25,
shadowColor: '#6366f1',
shadowOffsetX: 0,
shadowOffsetY: 0,
opacity: 0.9,
rotation: 0,
anchor: [0.5, 0.5],
duration: 3,
startTime: 0,
animations: ['fadeIn'],
});
scene.addCircle({
x: '50%', y: '50%',
radius: 100,
color: '#ffffff',
fill: true,
borderWidth: 2,
borderColor: '#8b5cf6',
opacity: 0.8,
rotation: 0,
anchor: [0.5, 0.5],
duration: 3,
startTime: 0,
animations: ['zoomIn'],
});
scene.addImage({
src: './assets/logo.png',
x: '50%', y: '50%',
width: 200,
height: 200,
fit: 'cover', // cover/contain/fill/none
borderRadius: 0,
flipX: false,
flipY: false,
brightness: 1, // 0-2
contrast: 1, // 0-2
saturation: 1, // 0-2
duration: 3,
startTime: 0,
animations: ['fadeIn'],
});
scene.addVideo({
src: './assets/clip.mp4',
x: 0, y: 0,
width: 1920,
height: 1080,
cutFrom: 0, // 裁剪起始(秒)
cutTo: undefined, // 裁剪结束(秒)
speedFactor: 1, // 播放速度
loop: false,
mute: true,
volume: 1,
duration: 10,
startTime: 0,
});
scene.addSVG({
src: './assets/icon.svg',
x: '50%', y: '50%',
width: 100,
height: 100,
fit: 'contain',
preserveAspectRatio: true,
enableSVGAnimations: true,
duration: 3,
startTime: 0,
animations: ['zoomIn'],
});
使用 Takumi 渲染任意 HTML/CSS 为视频元素,特别适合使用 CSS @keyframes 动画。
npm install @takumi-rs/core @takumi-rs/helpers
scene.addHtml({
x: 0, y: 0, width: 1280, height: 720, anchor: [0, 0],
html: `
<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:#1e1b4b;">
<h1 style="color:white;font-size:64px;">Hello World</h1>
</div>
`,
duration: 5,
});
通过 timeMs 自动注入,CSS 动画与视频时间轴同步:
scene.addHtml({
x: 0, y: 0, width: 1280, height: 720, anchor: [0, 0],
html: `
<style>
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.8; }
}
.spinner {
width: 120px; height: 120px;
border: 8px solid rgba(255,255,255,0.1);
border-top-color: #a78bfa;
border-radius: 50%;
animation: spin 1s linear infinite;
}
.title {
font-family: system-ui;
color: white;
font-size: 64px;
animation: pulse 2s ease-in-out infinite;
}
</style>
<div style="width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;">
<div class="spinner"></div>
<div class="title">Loading</div>
</div>
`,
duration: 5,
});
HTMLElement 默认开启中文字体支持,无需手动指定 fonts:
// ✅ 直接使用,默认就有中文支持
scene.addHtml({
html: `<div style="font-size:48px;">你好世界</div>`,
duration: 4,
// 无需指定 fonts
});
// ✅ 自定义 fonts 覆盖默认
scene.addHtml({
html: `<div style="font-family:'Microsoft YaHei';font-size:48px;">中文标题</div>`,
duration: 4,
fonts: [
{ path: 'C:/Windows/Fonts/msyh.ttc', family: 'Microsoft YaHei' },
{ path: 'C:/Windows/Fonts/simhei.ttf', family: 'SimHei' },
],
});
每个 Scene 必须在不同的 track 上:
// Scene 1: 0-4 秒(loading 动画)
builder.createTrack()
.createScene({ duration: 4 })
.addBackground({ color: '#1e1b4b' })
.addHtml({ x: 0, y: 0, width: 1280, height: 720, anchor: [0, 0],
html: loadingHtml, duration: 4 });
// Scene 2: 4-8 秒(CSS @keyframes 动画)
builder.createTrack() // ⚠️ 必须用新 track
.createScene({ duration: 4, startTime: 4 })
.addBackground({ color: '#0c0a09' })
.addHtml({ x: 0, y: 0, width: 1280, height: 720, anchor: [0, 0],
html: keyframeHtml, duration: 4,
fonts: [{ path: 'C:/Windows/Fonts/msyh.ttc', family: 'Microsoft YaHei' }] });
await builder.render('output/html-keyframes.mp4', { parallel: true });
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
html | string | - | HTML 字符串 |
node | object | - | Takumi node tree |
x | number|string | 0 | X 坐标 |
y | number|string | 0 | Y 坐标 |
width | number|string | 800 | 元素宽度 |
height | number|string | 600 | 元素高度 |
anchor | [number, number] | [0.5, 0.5] | 锚点(左上=0,0;中心=0.5,0.5) |
opacity | number | 1 | 透明度 0-1 |
rotation | number | 0 | 旋转角度(度) |
duration | number | - | 元素显示时长(秒) |
startTime | number | 0 | 元素起始时间(秒) |
timeOffset | number | 0 | CSS 动画起始偏移(秒) |
fonts | Array | - | 字体配置数组 |
stylesheets | Array<string> | - | 外部样式表 URL |
keyframes | object | - | 结构化 keyframe 定义 |
devicePixelRatio | number | - | 设备像素比 |
// URL 方式
fonts: ['https://fonts.googleapis.com/css2?family=Roboto']
// 系统字体路径
fonts: [{ path: 'C:/Windows/Fonts/msyh.ttc', family: 'Microsoft YaHei' }]
// Buffer 方式
fonts: [{ data: fontBuffer }]
scene.addHtml({
html: '<div style="color:white;font-size:48px;">Welcome</div>',
duration: 5,
animations: [
{ type: 'transform', from: { opacity: 0, x: -100 }, to: { opacity: 1, x: 0 }, duration: 1, easing: 'easeOut' },
'fadeIn',
],
});
import { renderHtmlFrame } from 'fkbuilder';
const rgba = await renderHtmlFrame({
html: '<div style="color:red;">Test</div>',
width: 1280,
height: 720,
timeMs: 0,
});
// rgba: width * height * 4 的 Buffer(RGBA 格式)
# 1. 安装 Tailwind v4
npm install -D tailwindcss @tailwindcss/cli
# 2. 创建 tailwind-input.css
# @import "tailwindcss";
# @theme {
# --color-brand: #FF6B6B;
# }
# 3. 编译
npx tailwindcss build -i tailwind-input.css -o output/tailwind/tailwind.css --minify
import fs from 'fs-extra';
const tailwindCss = fs.readFileSync('output/tailwind/tailwind.css', 'utf8');
scene.addHtml({
x: 0, y: 0, width: 1280, height: 720, anchor: [0, 0],
html: `
<style>${tailwindCss}</style>
<style>
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
.float { animation: float 3s ease-in-out infinite; }
</style>
<div class="min-h-screen bg-gradient-to-br from-purple-600 to-blue-500 flex items-center justify-center">
<h1 class="text-7xl font-bold text-white float">FKbuilder + Tailwind</h1>
</div>
`,
duration: 5,
});
✅ 完全离线,所有 utility 类生效,自定义 @keyframes 正常。
CDN 方式(更快但功能受限):
html: `<script src="https://cdn.tailwindcss.com"></script>
<div class="bg-gradient-to-br from-purple-600 to-blue-500 ...">`
⚠️ Takumi 不执行 <script>,hover 等 JS 事件不生效,但 CSS 样式和动画正常。
完整示例:examples/html-element-with-tailwind.js
anchor 控制元素"哪个角"对齐到 (x, y):
anchor: [0, 0] → 元素左上角对齐 (x, y)anchor: [0.5, 0.5] → 元素中心点对齐 (x, y)anchor: [1, 1] → 元素右下角对齐 (x, y)全屏 HTML 必须用 anchor: [0, 0] + x: 0, y: 0:
// ✅ 正确:全屏 HTML 左上角对齐画布左上角
.addHtml({
x: 0, y: 0, width: 1280, height: 720, anchor: [0, 0],
html: '...',
})
// ❌ 错误:anchor 默认值 [0.5, 0.5] + x: 0, y: 0
// 会导致元素偏移 (-640, -360),元素一半在画布外看不见!
.addHtml({
x: 0, y: 0, width: 1280, height: 720,
// anchor 默认为 [0.5, 0.5],元素中心对齐到 (0, 0)
// 元素最终位置: x = 0 - 1280*0.5 = -640 (超出画布)
html: '...',
})
居中显示元素的两种方式:
// 方式 1: anchor [0, 0] + x/y 指向画布中心
.addHtml({ x: 640, y: 360, width: 200, height: 100, anchor: [0, 0], html: '...' })
// 方式 2: anchor [0.5, 0.5] + x/y 指向元素中心(即画布中心 640, 360)
.addHtml({ x: 640, y: 360, width: 200, height: 100, anchor: [0.5, 0.5], html: '...' })
createTrack(),否则会被合并到同一图层导致时间累加错误parallel: true 时正常工作,Takumi 在每个 Worker 中独立初始化fontsfonts 时,CSS 中 font-family 必须和 fonts 数组中的 family 名称一致builder.build() 后又调用 builder.render() 会触发重复构建examples/html-element-basic.js、examples/html-element-keyframes.js、examples/html-element-with-font.js、examples/html-element-with-tailwind.jsscene.addAudio({
src: './assets/music.mp3',
volume: 0.8,
fadeIn: 2, // 淡入时长(秒)
fadeOut: 2, // 淡出时长(秒)
cutFrom: 0,
cutTo: undefined,
loop: false,
startTime: 0,
duration: 30,
});
从 LRC 文件加载歌词字幕,支持多时间标签格式 [03:06.90][02:33.64]歌词内容。
// 方式1:从 LRC 文件加载(推荐)
scene.addLRC('./assets/lyrics.lrc', {
x: '50%', y: '85%',
fontSize: 48,
fontFamily: 'Microsoft YaHei',
textColor: '#ffffff',
textAlign: 'center',
split: 'line',
splitDuration: 0.3,
});
// 方式2:使用 addSubtitles 加载 LRC
scene.addSubtitles({
src: './assets/lyrics.lrc',
x: '50%', y: '85%',
fontSize: 48,
fontFamily: 'Microsoft YaHei',
textColor: '#ffffff',
textAlign: 'center',
});
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
x | number/string | '50%' | X坐标 |
y | number/string | '85%' | Y坐标 |
fontSize | number | 48 | 字体大小 |
fontFamily | string | 'PatuaOne' | 字体名称 |
textColor | string | '#ffffff' | 文字颜色 |
textAlign | string | 'center' | 对齐方式 |
split | string | 'line' | 拆分类型: letter/word/line |
splitDuration | number | 0.3 | 拆分动画时长 |
maxLength | number | 20 | 每行最大字符数 |
音频可视化波形显示,支持多种样式。
scene.addOscilloscope({
audioPath: './assets/music.mp3', // 音频文件路径
x: '50%', y: '50%',
width: 400,
height: 200,
waveColor: '#00ff00', // 波形颜色
backgroundColor: 'rgba(0, 0, 0, 0.3)', // 背景颜色
lineWidth: 2, // 线条宽度
smoothing: 0.3, // 平滑度 (0-1)
mirror: true, // 是否镜像显示
style: 'line', // 样式类型
sensitivity: 1.0, // 灵敏度
cutFrom: 0, // 裁剪起始(秒)
cutTo: undefined, // 裁剪结束(秒)
windowSize: 0.1, // 显示窗口大小(秒)
scrollSpeed: 1.0, // 滚动速度
duration: 30, // 持续时间
startTime: 0,
});
| 样式 | 说明 |
|---|---|
line | 线条波形 |
bars | 柱状图 |
circle | 圆形波形 |
spectrum | 频谱 |
particles | 粒子效果 |
waterfall | 瀑布图 |
spiral | 螺旋形 |
ripple | 涟漪效果 |
grid | 网格 |
explosion | 爆炸效果 |
blob | 变形效果 |
rotating3d | 3D旋转 |
trail | 轨迹效果 |
weave | 编织效果 |
lightwave | 光波效果 |
particleflow | 粒子流 |
scene.addSubtitles({
text: 'Hello World', // 直接文本内容
x: '50%', y: '85%',
fontSize: 72,
fontFamily: 'PatuaOne',
textColor: '#ffffff',
position: 'center',
textAlign: 'center',
split: 'letter',
splitDelay: 0.1,
splitDuration: 0.3,
maxLength: 20,
duration: 5,
startTime: 0,
});
使用字符串数组形式:
animations: ['zoomIn']
animations: ['fadeInUp']
animations: ['bigIn', 'bigOut'] // 多个动画组合
| 名称 | 效果 |
|---|---|
fadeIn | opacity 0→1 |
fadeOut | opacity 1→0(在结束前1秒自动应用) |
fadeInUp | opacity 0 + Y偏移50 → opacity 1 |
fadeInDown | opacity 0 + Y偏移-50 → opacity 1 |
fadeOutUp | opacity 1 → opacity 0 + Y偏移-50 |
fadeOutDown | opacity 1 → opacity 0 + Y偏移50 |
| 名称 | 效果 |
|---|---|
zoomIn | scale 0→1 |
zoomOut | scale 1→0 |
bigIn | scale 2+opacity 0 → scale 1+opacity 1 |
bigOut | scale 1+opacity 1 → scale 2+opacity 0 |
zoomInLeft | 左上角缩放进入 |
zoomInFade | scale 0.5+opacity 0 → scale 1+opacity 1 |
zoomRotateIn | 缩放+旋转组合 |
| 名称 | 效果 |
|---|---|
rotateIn | rotation -180→0 |
rotateOut | rotation 0→180 |
rotateInLeft | 左旋+淡入 |
rotateInRight | 右旋+淡入 |
| 名称 | 效果 |
|---|---|
slideInTop | 从顶部滑入 |
slideInBottom | 从底部滑入 |
slideInLeft | 从左侧滑入 |
slideInRight | 从右侧滑入 |
| 名称 | 效果 |
|---|---|
bounceIn | 弹跳进入 |
bounceOut | 弹跳出 |
bounceInUp | 向上弹跳 |
bounceInLeft | 向左弹跳 |
| 名称 | 效果 |
|---|---|
flipInX | X轴翻转淡入 |
flipInY | Y轴翻转淡入 |
pulse | 脉动缩放 |
shake | 水平抖动 |
flash | 闪烁 |
swing | 摇摆 |
// 不指定 startTime,系统自动推断转场位置
track.addTransition({ name: 'CrossZoom', duration: 0.5 });
track.addTransition({ name: 'Dreamy', duration: 0.5 });
track.addTransition({ name: 'fade', duration: 0.5 });
// 指定 startTime(可选)
track.addTransition({ name: 'CrossZoom', duration: 0.5, startTime: 2.5 });
重要:转场在 GL 层面预渲染 fromFrame 和 toFrame,然后混合。
fromScene 元素的 endTime 会被调整为 transitionStartTime(转场开始时间)toScene 的 startTime 会被覆盖为 transitionEndTime(转场结束时间)预渲染帧时间点:
fromFrame 在 transitionStartTime - 0.001s 渲染(确保 fromScene 元素仍可见)toFrame 在 transitionEndTime 渲染(此时 toScene 元素已可见)当不指定 startTime 时:
transitionStartTime = sceneEndTime - duration/2transitionEndTime = sceneEndTime + duration/2例如:场景1 (0-3s) + 转场 (0.5s) + 场景2 (3-6s)
| 转场 | 说明 |
|---|---|
CrossZoom | 缩放交叉 |
Dreamy | 梦幻效果 |
GridFlip | 网格翻转 |
CircleOpen | 圆形展开 |
fade | 淡入淡出 |
wipeRight | 向右扫出 |
wipeLeft | 向左扫出 |
wipeUp | 向上扫出 |
wipeDown | 向下扫出 |
Swirl | 漩涡效果 |
ZoomInCircles | 圆形缩放 |
Mosaic | 马赛克 |
scene.addCircle({
x: '50%', y: '50%',
radius: 100,
duration: 5,
onFrame: (el, ev, item) => {
// el: 元素实例
// ev: 事件对象 { time, delta, count }
// item: Paper.js 渲染项
// 旋转
item.rotation = ev.time * 30;
// 脉冲缩放
const scale = 1 + Math.sin(ev.time * 4) * 0.1;
item.scaleX = scale;
item.scaleY = scale;
// 闪烁
item.opacity = 0.3 + Math.sin(ev.time * 5) * 0.4;
},
});
builder.build() 会修改原始元素的 startTime 和 endTime 属性。如果先调用 build() 查看结构,再调用 builder.render()(内部又会调用 build()),元素时间会重复累加。
修复:Track.build 内部使用 _absoluteTimeSet 标记防止重复处理,但最佳实践是:直接调用 builder.render(),不要额外手动调用 builder.build()。
// ❌ 错误:先 build 再 render,会导致时间累加
const vm = builder.build(); // 第一次 build
await builder.render(path); // 第二次 build(render 内部调用)
// ✅ 正确:直接 render
await builder.render(path);
元素使用 < 而非 <= 判断结束:
isActiveAtTime(time) {
return this.visible && time >= this.startTime && time < this.endTime;
}
这意味着元素在 endTime 时刻不再可见。
scene.startTime: 场景开始时间(绝对时间)element.startTime: 元素相对于场景的开始时间element.duration: 元素的持续时间element.endTime: 元素的结束时间 = sceneStartTime + elementStartTime + duration重要:元素的 endTime 不能超过场景的结束时间。超过的部分会被自动截断,并输出警告。
// 错误示例:元素的 endTime 超过了场景的结束时间
const scene = track.createScene({ duration: 3, startTime: 0 });
scene.addText({
text: 'Hello',
duration: 5, // 错误!元素持续时间超过场景时间
});
// 正确示例:元素的 endTime 应在场景范围内
const scene = track.createScene({ duration: 3, startTime: 0 });
scene.addText({
text: 'Hello',
duration: 3, // 正确:元素持续时间等于场景时间
});
转场时,系统以转场调整后的场景结束时间为准:
坐标支持 "50%" 形式,表示画布中心:
x: '50%', y: '50%' // 画布中心
x: '0%', y: '0%' // 左上角
x: '100%', y: '100%' // 右下角
scene.startTime,系统会基于前一个场景的结束时间自动推断toScene.startTime 会被强制覆盖为 transitionEndTime音频元素通常应该添加到独立的音频轨道,或者在场景外添加:
// 方式1:全局音频
composition.addAudio({ src: './music.mp3', startTime: 0, volume: 0.5 });
// 方式2:通过 builder 的 audioTracks
builder.audioTracks = [{ src: './music.mp3', startTime: 0, volume: 0.5 }];
SVG 元素需要异步加载,如果渲染时 SVG 还未加载完成,会导致问题。使用 initialize() 预加载:
await builder.initialize(); // 预加载所有资源
await builder.render(outputPath);
如果使用自定义字体,需要先注册:
import { registerFont } from 'fkbuilder';
registerFont('./fonts/CustomFont.ttf', 'CustomFont');
import { VideoBuilder } from 'fkbuilder';
import path from 'path';
async function createPromoVideo() {
const builder = new VideoBuilder({
width: 1280,
height: 720,
fps: 30,
});
const track = builder.createTrack({ zIndex: 1 });
// 场景 1: 标题 (0s - 3s)
const scene1 = track.createScene({ duration: 3, startTime: 0 });
scene1.addBackground({ color: '#0f0f23' });
scene1.addText({
text: 'FOLIKO',
x: '50%', y: '40%',
fontSize: 120, fontFamily: 'Arial Black', fontWeight: 'bold',
color: '#ffffff', textAlign: 'center', textBaseline: 'middle',
duration: 3,
animations: ['zoomIn'],
textShadow: true, textShadowColor: '#6366f1', textShadowBlur: 30,
});
// 场景 2: 特性展示 (3s - 6s)
const scene2 = track.createScene({ duration: 3, startTime: 3 });
scene2.addBackground({ color: '#1a1a2e' });
scene2.addText({
text: '40+ 插件',
x: '50%', y: '30%',
fontSize: 48, fontFamily: 'Arial', fontWeight: 'bold',
color: '#8b5cf6', textAlign: 'center',
duration: 3, animations: ['fadeInUp'],
});
// 转场(不指定 startTime,系统自动推断)
track.addTransition({ name: 'CrossZoom', duration: 0.5 });
track.addTransition({ name: 'Dreamy', duration: 0.5 });
const outputPath = './output/promo.mp4';
const result = await builder.render(outputPath, {
parallel: false,
usePipe: true,
});
console.log(`渲染完成: ${result}`);
}
createPromoVideo().catch(console.error);
project/
├── output/
│ └── video.mp4
├── examples/
│ └── render-video.mjs
└── assets/
├── music.mp3
├── image.png
└── icon.svg