mcpField
C
cavalry

cavalry

github.com/jablonkai创意与媒体447 次安装今天更新
代码仓库

关于 cavalry

通过 JavaScript 脚本和 Cavalry Bridge 控制 Cavalry 动画软件的技能。它利用 API 模式和辅助脚本构建、检查并渲染场景,支持迭代预览。

主要功能

  1. 01通过 JavaScript 控制 Cavalry
  2. 02使用 API 辅助脚本构建场景
  3. 03创建关键帧与缓动
  4. 04检查渲染结果和场景数据
  5. 05渲染 PNG 帧序列

适用场景

  1. 01构建动画场景
  2. 02制作文字或形状动画
  3. 03预览并修正合成
  4. 04渲染帧序列

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jablonkai/skills --skill cavalry

安装后在 Claude Code 中输入/cavalry确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-reference.md
references/cavalry-types/
deformer.d.ts
emitter.d.ts
LICENSE
modifier.d.ts
plugin.d.ts
README.md
render.d.ts
script.d.ts
shape.d.ts
utility.d.ts
references/cavalry-types/namespaces/
api.d.ts
cavalry.d.ts
console.d.ts
ctx.d.ts
def.d.ts
install.d.ts
plugin.d.ts
render.d.ts
ui.d.ts
scripts/
cavalry-bridge.js
cavalry-helpers.js
cavalry-send.sh

详细介绍

Cavalry 控制

Cavalry(/Applications/Cavalry.app)支持使用 JavaScript 编写脚本(自 2.6 起采用 V8 12.4,因此支持 ES2024——let/const、箭头函数和 Set 方法均可使用;提供 api.* / ui.* / cavalry.* 模块)。通过 Cavalry Bridge 控制它——这是一个运行在 Cavalry 内部的小型脚本 UI,可执行 POST 到 127.0.0.1:8731 的任意内容。以下所有内容均已在真实项目中通过 Cavalry 2.7.2(2026 年 5 月发布的当前版本)验证;其余 API 内容请参阅参考文件。

自 2.7 起,Cavalry 归 Canva 所有:登录需要使用 Canva 账号,原先的 Professional 功能现已向所有人免费开放——因此无需考虑许可等级。唯一的变化是 2.7.x 不再附带 CLI(参见“渲染”)。

  • references/api-reference.md — 精简的模块参考(api、cavalry、ui、WebClient/WebServer、渲染脚本、CLI)。需要使用下方速查表未涵盖的内容时,请查阅此文件。
  • references/cavalry-types/ — 以随附 TypeScript 定义和 JSDoc 示例呈现的完整 API;可在其中搜索参考文档未涵盖的函数。
  • scripts/cavalry-bridge.js — 需安装到 Cavalry 中的桥接程序。
  • scripts/cavalry-helpers.js — setAttrs/keyAttrs(设置属性和关键帧,失败时会明确报错,而非静默失败)、newComp/useComp/findComp、splitLetters(逐字母文本)、renderFrames、writeJSON。在每个构建脚本顶部通过 api.load("<abs path>/scripts/cavalry-helpers.js") 引入。
  • scripts/cavalry-send.sh — 将 JS 文件(或 -c 'inline code')发送到桥接程序并等待执行完成;失败时返回非零退出码并打印 JS 错误。--ping 用于检查桥接程序是否可访问,并报告桥接程序和 Cavalry 的版本。

控制循环

  1. 用户启动桥接程序(每个会话只需一次):Cavalry 必须正在运行,并从其 Scripts 菜单启动 Cavalry Bridge.js。无法远程执行此操作——如果 bash scripts/cavalry-send.sh --ping 没有收到回复,请让用户打开 Cavalry 并运行 Scripts ▸ Cavalry Bridge。首次安装桥接程序时,将 scripts/cavalry-bridge.js 复制到 Cavalry Scripts 文件夹,并命名为 Cavalry Bridge.js(~/Library/Application Support/Cavalry/Scripts/——如果文件夹不存在,请创建;Help ▸ Show Scripts Folder 可确认路径)。如果 --ping 有回复但没有 "version" 字段,则表示运行的是旧版桥接程序:用当前版本覆盖它并重启。
  2. 将构建脚本写入暂存区并发送: bash scripts/cavalry-send.sh /path/to/build.js。每次发送都会获得一个请求 ID,并在用户的 Cavalry 偏好设置文件夹中等待对应的结果文件(该路径通过桥接程序的 GET /get 回复发现),因此多个调用方可以安全地共用一个桥接程序。长时间渲染需要设置 CAVALRY_SEND_TIMEOUT=600(秒)。
  3. 反馈通过文件返回,而不是通过 HTTP。 桥接程序会报告成功或失败,以及抛出错误的异常消息和堆栈;console.log 输出会留在 Cavalry 的 Log 窗口中。因此,每个脚本都必须将结果写入磁盘,以便读取:
    • 视觉状态 → 在几个关键帧调用 api.setFrame(f); api.renderPNGFrame(dir + "/check_" + f, 50)(50 = 50% 缩放预览),然后读取 PNG。应在构建完成后单独发送渲染脚本——脚本中创建的连接(变形器、错开效果)会等到脚本返回后才进行评估,因此同一次发送中的预览可能不准确;
    • 数据(ID、边界框、属性值)→ api.writeToFile(path, JSON.stringify(x), true)。
  4. 迭代:检查预览、修复脚本并重新发送。构建脚本应可重复运行——每个版本都创建新的合成(api.createComp("Thing v2")),避免重复运行时将图层叠加到旧合成中。当前合成属于共享 UI 状态(用户或其他调用方可能会在两次发送之间更改它),因此,任何在后续发送中进行渲染或编辑的脚本都应先按名称查找其合成——使用辅助函数中的 useComp("Thing v2")。
  5. 最终渲染:PNG 帧循环 + ffmpeg(见下文“渲染”)。

发送失败时,stderr 会输出 Cavalry error: <message + stack>——行号与发送的脚本相对应。如果失败但没有消息,则表示存在语法错误(脚本尚未启动)或权限提示被拒绝:使用 node --check build.js 检查,并请用户查看 Cavalry 是否有打开的对话框。如果发送在渲染期间超时,通常只是渲染耗时较长——应延长超时时间,而不是重新发送,否则会运行两次渲染。

场景构建速查表(经验证的模式)

api.set 从不报错——检查名称,不要猜测

如果图层没有某个属性,api.set 会静默忽略:属性名称拼写错误,或名称来自旧版 Cavalry,都会被直接忽略,发送仍会报告成功。因此,使用 try/catch 回退链(“尝试 frameRate,否则用 fps”)毫无用处——第一个猜测总会“成功”。请使用辅助函数中的 setAttrs(id, {...})(它会检查 api.hasAttribute,并抛出错误,指出属性名称和图层类型);如果不确定,请使用 api.getAttributes(id) 列出实际名称。

合成设置
api.load("/abs/path/to/skills/cavalry/scripts/cavalry-helpers.js");
var comp = newComp("My animation v1", {width: 1920, height: 1080, fps: 30, frames: 240,
                                       background: "#121212"});   // or "transparent"

即 api.createComp + api.setActiveComp + resolution、fps、startFrame/endFrame(包含首尾帧——240 帧即 0–239)和 backgroundColor。新建合成默认使用不透明背景(白色,除非用户偏好设置不同):设置 backgroundColor 即可使用纯色——无需添加超大背景矩形;设为 "#00000000"(alpha 0)则可输出透明背景。

形状和属性
var rect = api.primitive("rectangle", "Bar");        // also: "ellipse", "polygon", "star"...
setAttrs(rect, {"generator.dimensions": [400, 60],   // rectangle: [w, h]
                "position": [0, 0],                  // origin = comp centre, +y = UP
                "material.materialColor": "#00ADEF"});
var dot = api.primitive("ellipse", "Dot");
setAttrs(dot, {"generator.radius": [20, 20]});        // ellipse: radius [rx, ry], not dimensions

常见属性路径:position [x,y]、position.x、scale.x/scale.y、rotation.z、 opacity、material.materialColor(十六进制字符串)、material.alpha(0–100 范围;值 ≤ 1 时几乎不可见)、generator.dimensions(矩形)、generator.radius(椭圆)。 要查找名称:api.getAttributes(id)、api.getAttributeDefinition(id, attr)(类型、 默认值、枚举值),或在 UI 中右键单击属性 → 复制脚本路径。

可靠的尺寸调整:测量,而非猜测

文本和导入的图像没有可设置的尺寸属性,因此请测量实际尺寸并用缩放归一化:

var bb = api.getBoundingBox(layer, true);          // world space: {x,y,width,height,left,right,top,bottom,centre}
var s = 470 / bb.height;                           // e.g. scale an image or title to 470 px tall
setAttrs(layer, {"scale.x": s, "scale.y": s});

在世界空间中,top > bottom(+y 方向朝上)。请在设置缩放关键帧之前测量。

关键帧和缓动
api.keyframe(layer, 22, {"position.y": 940});
api.keyframe(layer, 50, {"position.y": 150});
api.magicEasing(layer, "position.y", 22, "BounceOut");   // easing is applied per-attr AT a keyframe

api.keyframe 只接受标量叶属性。 数组——{"position": [x, y]}、 {"generator.dimensions": [w, h]}——或用于向量属性的单个数字—— {"rotation": 45}(从 2.x 起,rotation 包含 x/y/z;2D 旋转使用 rotation.z)——都不会创建 关键帧,也不会报错。请直接为 position.x/position.y、scale.x、rotation.z 等设置关键帧, 或使用辅助函数中的 keyAttrs(id, frame, {...}),它会拆分数组,将单个 rotation 值映射到 rotation.z,并拒绝未知名称。magicEasing 同样指定单个叶属性 ("rotation.z",而不是 "rotation");可通过 api.getKeyframeTimes(id, "position.x") 检查关键帧。 (api.set 更宽容:数组和单个 rotation 数值都可用。)

Magic easing 预设:SlowIn/SlowOut/SlowInSlowOut、VerySlowIn/VerySlowOut/VerySlowInVerySlowOut、 SpringIn/SpringOut/SpringInSpringOut、SmallSpring… 变体、AnticipateIn、OvershootOut、 AnticipateInOvershootOut、BounceIn/BounceOut/BounceInBounceOut、None。 制作经验:文字弹入时用 OvershootOut,下落/着陆时用 BounceOut, 爆发效果(粒子、尘埃)用 SlowOut,漂移和脉动时用 SlowInSlowOut。

简易粒子爆发(无需粒子系统):创建 N 个小椭圆,从同一原点沿 Math.cos/sin(angle) * dist 向外运动, 缩放至 0,使用 SlowOut——完整的彩纸/火花代码片段请参阅参考资料。

文本——以及逐字母动画方法
var t = api.create("textShape", "Title");
setAttrs(t, {"text": "EMU 6-DAY RACE",
             "horizontalAlignment": 1, "verticalAlignment": 1,   // 1 = centre
             "fontSize": 150, "font.font": "Nebula Sans", "font.style": "Black",
             "material.materialColor": "#FFFFFF", "autoWidth": true, "autoHeight": true,
             "position": [0, -190]});

如果没有 autoWidth,文本框会换行显示过长的行。Unicode(重音符号、ő/ű)可直接使用。使用字体前请先检查: cavalry.fontExists(family, style)(两个参数都必填)和 cavalry.getFontStyles(family);如果字体不存在, 请改用已存在的字体,避免 Cavalry 静默替换。

逐字母动画(每个字形依次下落)——保留一个文本图层,并连接 Sub-Mesh 变形器 + 用于驱动其时间偏移的 Stagger。Sub-Mesh 默认使用 Level Mode Text (Characters)(levelMode 为 3),因此每个字形都会获得一份独立的 Sub-Mesh 关键帧副本, 并由 stagger 逐个错开:

var subMesh = api.create("subMesh", "Title bounce");
api.keyframe(subMesh, 34, {"shapePosition.y": 990});   // start above frame (+y = up)
api.keyframe(subMesh, 64, {"shapePosition.y": 0});
api.magicEasing(subMesh, "shapePosition.y", 34, "BounceOut");
api.connect(subMesh, "id", t, "deformers");
api.parent(subMesh, t);

var stagger = api.create("stagger", "Title stagger");
setAttrs(stagger, {"minimum": -12, "maximum": 0});     // frames of per-letter offset
api.flipGraph(stagger, "graph", "vertical");           // so the FIRST letter lands first
api.connect(stagger, "id", subMesh, "shapeTimeOffset");
api.parent(stagger, t);

请在单独的一次发送中预览。 Cavalry 只有在创建连接(变形器、stagger、行为)的脚本返回后才会计算这些连接—— 在同一次发送中渲染的帧会显示所有字母一起移动,看起来就像 stagger 出了问题。请在一次发送中构建, 然后在下一次发送中调用 useComp(...) + 渲染。同样,api.get 返回的 Sub-Mesh 值不会包含 stagger 效果; 只有渲染帧才能证明逐字母动画有效。

如果每个字形都需要单独的缓动、颜色或路径(或需要通过脚本重新排版),辅助函数中的 splitLetters(str, style, cx, cy) 会为每个字形创建一个文本图层,并将其放置在完整字符串中的准确位置 (包括字距调整);为每个图层设置各自的起始帧:

var letters = splitLetters("NIGHT RUN 2026",
    {"fontSize": 150, "font.font": "Helvetica", "font.style": "Bold",
     "material.materialColor": "#FFFFFF"}, 0, 40);          // line centred on [0, 40]
letters.forEach(function (id, i) {
    var y = api.get(id, "position.y"), f0 = 10 + i * 3;     // 3-frame stagger
    api.keyframe(id, f0, {"position.y": y + 700});
    api.keyframe(id, f0 + 20, {"position.y": y});
    api.magicEasing(id, "position.y", f0, "BounceOut");
});
图像和其他素材
var asset = api.loadAsset("/abs/path/logo.png", false);   // false = not an image sequence
var layer = api.addAssetToComp(asset);
var bb = api.getBoundingBox(layer, true);
var s = 470 / bb.height;                                  // scale to ~470px tall
setAttrs(layer, {"scale.x": s, "scale.y": s});

数据驱动场景:api.jsonFromAsset(assetId) 可解析 JSON/CSV 素材, api.loadGoogleSheet(spreadsheetId, sheetId) 可读取实时表格,而 api.WebClient 可从任意 HTTP API 获取数据——详见参考资料。

渲染

默认方式:PNG 帧循环 + ffmpeg。 除桥接外无需其他工具,可供你检查每一帧,随后可从自己的 Shell 中运行 ffmpeg:

api.load("/abs/path/to/skills/cavalry/scripts/cavalry-helpers.js");
useComp("My animation v1");
renderFrames(OUT, "name", 0, 239, 100);   // OUT/name_0000.png … name_0239.png

renderPNGFrame(pathWithoutExtension, scalePercent) 会渲染当前活动合成的帧;会自动追加 .png,且只渲染可见图层(独显状态也会生效——调用 api.soloLayers([]) 可清除)。渲染 240 帧 1080p 画面需要一段时间——请调高 CAVALRY_SEND_TIMEOUT。然后将帧合成为视频:

ffmpeg -framerate 30 -i name_%04d.png -c:v libx264 -pix_fmt yuv420p out.mp4       # opaque
ffmpeg -framerate 30 -i name_%04d.png -c:v qtrle out.mov                          # alpha overlay

如需输出透明背景,请为合成设置透明背景(newComp(..., {background: "transparent"}),即将 backgroundColor 设为 "#00000000")——如果合成保留默认设置,即使没有 BG 图层,渲染出的背景也会是不透明的白色。这样 PNG 会保留 Alpha 通道,qtrle .mov 可直接作为叠加素材导入 DaVinci Resolve(较小的替代方案是 ProRes 4444:-c:v prores_ks -profile:v 4 -pix_fmt yuva444p10le)。请验证: ffprobe -v error -show_entries stream=pix_fmt out.mov 应显示 argb/rgba,而非 rgb24。 请从 Shell 运行 ffmpeg,而不要通过 api.runProcess 运行——从桥接这类脚本 UI 调用该命令时,会弹出“信任此脚本”对话框,并一直阻塞,直到用户作出回应。 api.renderSVGFrame(path, scale, skipComps) 可用于生成矢量快照。

渲染队列(应用内):api.addRenderQueueItem(compId) → 配置 → api.render(itemId) 或 api.renderAll()。适用于输出原生 mp4/ProRes/GIF,也适用于批量渲染多个合成或场景(每个文件调用一次 api.openScene(path, true))。渲染队列项目可以附带 Setup/Pre/Post 渲染脚本(例如在每次渲染时替换素材或文本)——请参阅参考文档。

Cavalry CLI——2.7.x 中不可用。 Scene Group 的 2.7.0–2.7.2 版本说明称 CLI 缺失(“请继续使用当前版本……我们正在努力恢复此功能”),而在 2.7 之前,其 render/list 命令需要 Enterprise 许可证。2.7.2 版本包中仍有 CavalryCLI.app,但其中的 Contents/MacOS/ 为空——没有可调用的二进制文件。要以无头方式批量渲染,请改为通过桥接驱动正在运行的应用(打开每个场景、渲染、运行 ffmpeg);如果用户固定使用较旧版本,请参阅参考文档了解 2.7 之前的 CLI 参数。

踩坑经验(来之不易)

  • 桥接器采用“发后即忘”模式——围绕基于文件的反馈来设计每个脚本(状态 JSON、预览 PNG、错误捕获文件)。绝不要假设能看到 console.log。
  • 桥接器会将 api.exec 代码包装在 IIFE 中,因此顶层直接使用 var 没问题,但每次发送之间不会保留任何内容。请将状态保存在场景中或磁盘上。
  • api.set/api.keyframe 不会报错:未知名称会被忽略,不会产生错误;keyframe 还会忽略向量上的数组和裸数字——请使用 setAttrs/keyAttrs。本文件中的名称适用于 2.7.2(fps、endFrame、text、backgroundColor);此前有关 frameRate/string/frameRangeEnd 的说明不适用于 2.7。如果脚本还必须在旧版本中运行,请根据 api.getCavalryVersion() 进行分支判断。
  • 颜色:2.6 移除了 Linear Gamma(合成设置)和 Color Filter 着色器——请改用工作色彩空间为 Linear sRGB 的色彩管理。
  • material.alpha 的范围是 0–100,不是 0–1。
  • 新图层会添加到当前活动合成中。 api.create/api.primitive/addAssetToComp 会将图层添加到当前活动的合成中——创建任何内容之前都要先调用 newComp/useComp,即使是在一次性探测脚本中也不例外。
  • 堆叠和分组:后创建的图层会绘制在上方——先创建背景和底板,再创建其上的文本。若要将多个图层作为一个整体移动(例如字幕条底板、色条和文本),请使用 api.create("null", "Group") 创建空对象,并通过 api.parent 将这些图层设为其子级,然后对空对象添加动画。
  • 向量属性读取时会返回对象:api.get(id, "position") 返回 {x, y}(resolution、generator.radius 同理,backgroundColor 返回 {r,g,b,a}),而 api.set 接受 [x, y]。需要数字时,请读取 position.x/position.y。
  • 低成本预览:在几个关键情节点以 50% 比例渲染检查帧,而不是渲染每一帧;只有预览效果满意后,才渲染完整分辨率的整个范围。
  • 重新运行:每次尝试都通过 api.createComp 创建一个全新的带版本号的合成;在同一合成中重新运行会复制所有图层。
  • 在脚本中保存场景——api.saveSceneAs("/path/scene.cv")——这样即使发生崩溃,工作成果也能保留;如果模态对话框处于打开状态,该调用会返回 false(或抛出异常),因此请检查结果。未经询问,绝不要调用 api.newScene()/api.openScene()——它们会丢弃用户当前打开的场景。
  • 随附的类型定义也可作为离线文档——几乎每个函数都有 JSDoc 示例;猜测签名之前先用 grep 搜索。

安全

运行桥接器意味着在用户的计算机上运行一个代码执行服务器。要求用户启动之前,请明确告知这一点。

  • 桥接器绑定到 127.0.0.1:8731,并会在实时会话中执行 POST 到该地址的任何 JavaScript——使用用户的权限以及用户当前打开的场景。原始非 JSON 请求体会直接作为 JS 运行。请求没有身份验证:任何本地进程,以及共享计算机上的其他用户,都可以通过桥接器控制 Cavalry。
  • 网页无法访问。带有 Origin 标头或跨站 Sec-Fetch-Site 的请求会被拒绝并返回 403,因此用户浏览器中的网页无法访问桥接器。这项检查是唯一的防护措施——没有令牌。
  • 只有用户运行桥接器后,它才会开始监听。 将 cavalry-bridge.js 复制到 Scripts 文件夹只会将其添加到菜单中;只有用户选择 Scripts ▸ Cavalry Bridge 后,端口才会打开,并在该会话期间保持开启。
  • 要停止桥接器,请退出 Cavalry。 没有远程关闭功能;进程结束时端口会释放。

评论

支持 Markdown