惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
U
Unit 42
MyScale Blog
MyScale Blog
B
Blog
阮一峰的网络日志
阮一峰的网络日志
量子位
IT之家
IT之家
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
Recent Announcements
Recent Announcements
V
Visual Studio Blog
G
Google Developers Blog
Last Week in AI
Last Week in AI
雷峰网
雷峰网
博客园 - 聂微东
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
博客园 - 司徒正美
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
月光博客
月光博客
aimingoo的专栏
aimingoo的专栏

博客园 - lincats

3Blue1Brown:一个斯坦福毕业生的业余项目,如何成了全球最火的数学课堂 Caveman vs Ponytail:AI 编程圈"懒人哲学"两大门派正面交锋 /handoff,只有几行,却是Matt Pocock调用频率最高的 skill Matt Pocock 演讲逐帧拆解:好 Skill 和坏 Skill 的差距就在这 4 个维度 Remotion Skill vs HyperFrames Skill:AI视频圈两大技能包正面交锋,最好的对手,最好的搭档? grill-me 从入门到精通:Matt Pocock 技能宇宙中最出圈的那个"追问机器" Claude Code 最强Skills路由器:/ask-matt 完全上手指南(入门篇) 你的 AI 编程工具里,缺一个“专家团队“,一人公司必备的Agents合集 106 张镜头配方卡、161 段样片:这个 AI 技能让你的产品视频一秒拥有电影感 69万星、263万安装:一个「让 AI 别画得那么丑」的技能,怎么成了 Claude Code 设计类第一? SuperPower vs grill-me:AI编程圈两大skill正面交锋,你站哪边? Token 和上下文窗口——AI 记性的秘密 全球 AI 大佬都在用的“AI 使用说明书“:Andrej Karpathy 的 CLAUDE.md 究竟是什么? 上下文工程:AI 智能体为什么离不开它? 5个Claude Code Skill,把产品经理的反馈分析从45分钟压到了10分钟 Claude Code项目越写越乱?这套清理流程能救你 Claude Code再强,也有这7件事做不了 Skill用得好,下班走得早:一文讲透Skill的结构与设计 谷歌 75% 代码是 AI 写的,但 83% 的人记不住它写了什么AI 编程的效率神话背后,是一场集体认知的悄然退化 [Coze] 手工构建Coze时间线相关的代码 [COZE]文本分段提求词 [Coze]根据音频时间轴生成 [Coze] 剪映小助手整理时间线和素材的脚本 一步一步学习使用FireMonkey动画(6) 用实例理解动画的运行状态 一步一步学习使用FireMonkey动画(5) 动画图解11种动画插值类型 一步一步学习使用FireMonkey动画(4) 使用Delphi的基本动画组件类,路径和位图列表动画 弹跳小球和奔跑的小人示例 一步一步学习使用FireMonkey动画(3) 使用Delphi的基本动画组件类 一步一步学习使用FireMonkey动画(2) 使用TAnimator类创建动画 一步一步学习使用FireMonkey动画(1) 使用动画组件为窗体添加动态效果 一步一步学习使用LiveBindings(16)使用代码创建LiveBindings绑定
[扣子工作流一] 通过编程为米核剪映小助手添加关键帧
lincats · 2026-01-30 · via 博客园 - lincats

segment_infos1和segment_infos2是调用Add_images之后返回的segment_info片断。通过为每一个片段添加关键帧实现动画效果

async function main({ params }: Args): Promise {
// 1. 参数解析
const {segment_infos1, segment_infos2} = params
// params包含两个视频片段的信息,每个片段包含:
// - start: 片段开始时间(微秒)
// - end: 片段结束时间(微秒)
// - id: 片段唯一标识符

// 2. 为第一个视频片段生成关键帧
const segment_infos1_list = [];
for(let i=0;i<segment_infos1.length;i++){
const start = segment_infos1[i].start;
const end = segment_infos1[i].end;

// 2.1 生成垂直方向(Y轴)关键帧 - 上下跳动
// 每隔900000微秒(0.9秒)打一个关键帧
const countY = (end - start) / 900000;
for(let j=0;j<countY;j++){
// 交替值:-250px 和 -150px
let value = j%2==0 ? -250 : -150;
// 将像素值转换为归一化值(除以1080 - 视频高度)
segment_infos1_list.push({
"offset": j*900000, // 关键帧时间偏移(从片段开始计算)
"property": "KFTypePositionY", // 关键帧属性:Y轴位置
"segment_id": segment_infos1[i].id,
"value": value/1080 // 归一化后的值(-250/1080 ≈ -0.231, -150/1080 ≈ -0.139)
});
}

// 2.2 生成水平方向(X轴)关键帧 - 左右移动
// 每隔700000微秒(0.7秒)打一个关键帧
const countX = (end - start) / 700000;
for(let j=0;j<countX;j++){
// 交替值:-1024px 和 -824px
let value = j%2==0 ? -1024 : -824;
segment_infos1_list.push({
"offset": j*700000,
"property": "KFTypePositionX", // 关键帧属性:X轴位置
"segment_id": segment_infos1[i].id,
"value": value/1920 // 归一化后的值(除以1920 - 视频宽度)
});
}
}

// 3. 为第二个视频片段生成关键帧(与第一个片段参数不同)
const segment_infos2_list = [];
for(let i=0;i<segment_infos2.length;i++){
const start = segment_infos2[i].start;
const end = segment_infos2[i].end;

// 3.1 垂直方向关键帧(1秒间隔)
const countY = (end - start) / 1000000;
for(let j=0;j<countY;j++){
// 交替值:217px 和 117px
let value = j%2==0 ? 217 : 117;
segment_infos2_list.push({
"offset": j*1000000,
"property": "KFTypePositionY",
"segment_id": segment_infos2[i].id,
"value": value/1080
});
}

// 3.2 水平方向关键帧(0.8秒间隔)
const countX = (end - start) / 800000;
for(let j=0;j<countX;j++){
// 交替值:1049px 和 900px
let value = j%2==0 ? 1049 : 900;
segment_infos2_list.push({
"offset": j*800000,
"property": "KFTypePositionX",
"segment_id": segment_infos2[i].id,
"value": value/1920
});
}
}

// 4. 输出结果
const ret = {
"keyframes1": JSON.stringify(segment_infos1_list),
"keyframes2": JSON.stringify(segment_infos2_list)
};
return ret;
}