关于 creator-plugin-development
面向 LottieFiles Creator 插件开发者的开发技能,说明如何构建动画编辑器插件。内容涵盖操作场景的沙盒与 React UI 分层,以及 API 和消息通信实践。
主要功能
- 01沙盒与 UI 架构
- 02场景、图层和形状操作
- 03关键帧动画模式
- 04资源导入指引
- 05类型化消息传递
- 06开发与验证命令
适用场景
- 01搭建 Creator 插件
- 02创建或制作场景元素动画
- 03导入 SVG、图像或 Lottie 资源
- 04排查 UI 与插件间的通信问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lottiefiles/ creator-plugin-skills --skill creator-plugin-development安装后在 Claude Code 中输入/creator-plugin-development确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/architecture-and-communication.mdimporting-assets.mdnetwork-and-libraries.mdscene-graph-and-nodes.mdshapes-styling-animation.mdstorage-and-events.md详细介绍
Creator 插件开发
Creator 插件扩展了 LottieFiles Creator 动画应用。它们采用双部分沙盒架构:
- 插件沙盒(
plugin/plugin.ts)— 在隔离环境中运行,可访问creator全局 API。可以操作场景、图层、形状和关键帧。不能发起网络请求。 - UI(
src/)— 在 iframe 中渲染的标准 React 应用。可以通过fetch发起网络请求。无法访问creatorAPI。
这两部分仅通过消息传递进行通信。
项目结构
my-plugin/
├── plugin/
│ ├── manifest.json # Plugin metadata (id, name, apiVersion, entry, ui)
│ ├── plugin.ts # Sandbox code — has `creator` API access
│ └── [helpers].ts # Optional helper modules
├── src/
│ ├── main.tsx # React DOM entry point
│ ├── app.tsx # Main UI component
│ └── components/ # React components
├── vite.config.ts # Uses @lottiefiles/vite-plugin-creator
├── tsconfig.json # Root config with references
├── tsconfig.plugin.json # Plugin sandbox TypeScript config (no DOM)
├── tsconfig.app.json # UI TypeScript config (DOM + JSX)
├── index.html # Vite app template
└── package.json
tsconfig.plugin.json 用于编译沙盒代码(不含 DOM 库)。tsconfig.app.json 用于编译 UI 代码(包含 DOM 和 JSX)。
插件清单(plugin/manifest.json)定义插件的身份信息和入口点:
{
"id": "unique-uuid-v4",
"name": "My Plugin",
"apiVersion": "1",
"entry": "plugin.js",
"ui": "ui.html"
}
开发命令
npm create @lottiefiles/creator-plugin@latest my-plugin # Scaffold a new plugin
# From the plugin directory (e.g., plugins/my-plugin/):
npm run dev # Start dev server with HTTPS hot-reload
npm run build # TypeScript check + Vite production build
npm exec tsc -- -b # Type check (run before completing any task)
要在 Creator 中加载插件:选择 Plugins > Develop > New plugin,然后输入 npm run dev 提供的 localhost URL。
通信模式(重要)
这是最常见的错误来源。消息的包装方式不对称:
UI 向插件发送消息
// In UI code (src/app.tsx) — MUST wrap in pluginMessage object
parent.postMessage(
{ pluginMessage: { type: 'create-shape', color: '#ff0000' } },
'*'
);
插件接收消息
// In plugin sandbox (plugin/plugin.ts) — messages arrive unwrapped
creator.ui.onMessage((msg) => {
if (msg.type === 'create-shape') {
// Use creator API here
}
});
插件向 UI 发送消息
// In plugin sandbox — no wrapping needed
creator.ui.postMessage({ type: 'shape-created', layerId: layer.id });
UI 接收消息
// In UI code — messages arrive wrapped in pluginMessage
window.addEventListener('message', (event) => {
const message = event.data.pluginMessage;
if (message?.type === 'shape-created') {
// Handle response
}
});
类型安全的消息
定义共享消息类型,在编译时发现不匹配:
// shared/types.ts
export type PluginMessage =
| { type: 'create-shape'; color: string }
| { type: 'import-svg'; content: string }
| { type: 'delete-selection' };
如需跟踪请求和响应,请添加 messageId 字段。
关键 API 模式
初始化插件
creator.ui.show({ width: 300, height: 500 });
访问场景
const scene = creator.activeScene;
scene.size; // { width, height }
scene.duration; // seconds
scene.framerate; // FPS
scene.layers; // ReadonlyArray<Layer>
创建形状
const layer = creator.activeScene.createShapeLayer();
const rect = layer.createRectangle({ size: { width: 200, height: 150 } });
layer.createFill({ type: 'SOLID', color: { r: 66, g: 133, b: 244 } });
导入资源
// From URL
const anim = await scene.import({ type: 'LOTTIE', url: 'https://...' });
const img = await scene.import({ type: 'IMAGE', url: 'https://...' });
const svg = await scene.import({ type: 'SVG', url: 'https://...' });
// From content string
const svgLayer = await scene.import({ type: 'SVG', content: svgString });
// Audio, from a URL or base64 content
const audio = await scene.import({ type: 'AUDIO', content: base64Audio });
导入 LOTTIE 和 SVG 会返回 SceneLayer。导入 IMAGE 会返回 ImageLayer。导入 AUDIO 会返回 AudioLayer。导入失败时会拒绝 Promise,因此请使用 try/catch 包裹 scene.import()。
动画属性
layer.position.addKeyframes([
{ frame: 0, value: { x: 100, y: 100 } },
{ frame: 60, value: { x: 400, y: 100 } },
]);
// With easing
const easeInOut = { type: 'CUBIC_BEZIER', x1: 0.42, y1: 0, x2: 0.58, y2: 1 };
layer.position.addKeyframes([
{ frame: 0, value: { x: 50, y: 100 }, easing: easeInOut },
{ frame: 60, value: { x: 350, y: 100 } },
]);
选择
const selectedNodes = creator.selection.nodes;
creator.on('selection:nodes', (nodes) => {
creator.ui.postMessage({ type: 'selection-changed', count: nodes.length });
});
节点类型检查
执行操作前,请务必检查节点类型,并跳过不支持的类型。scene.layers、creator.selection.nodes、selection:nodes、shapes 和 creator.assets 可能会返回插件无法识别的类型,包括比已安装的 API 类型更新的类型。请参阅 references/scene-graph-and-nodes.md 中的“Unknown Node Types”:
const layers = creator.selection.nodes;
layers.forEach((node) => {
if (node.type === 'SHAPE_LAYER') {
// Shape layer operations (has .shapes, .fills, .strokes, .trimPaths)
} else if (node.type === 'IMAGE_LAYER') {
// Image layer operations (has .image)
} else if (node.type === 'SCENE_LAYER') {
// Scene layer operations (has .scene, .break())
} else if (node.type === 'TEXT_LAYER') {
// Text layer operations (has .text, .fontFamily, .fontStyle, .fontSize,
// .alignment, .fill/.createFill, .stroke/.createStroke)
} else if (node.type === 'NULL_LAYER') {
// Null layer operations (transform only, has .layers)
} else if (node.type === 'AUDIO_LAYER') {
// Audio layer operations (has .audio, .muted, .volume; no transform)
}
// Any other type: skip it
});
音频图层没有变换属性。在读取 scene.layers 或选择项中的图层的 position、opacity 或其他变换成员之前,请检查其类型,或使用 creator.utils.isTransformableLayer(layer)。请参阅 references/scene-graph-and-nodes.md。
网络请求
插件沙盒不能发起 fetch 请求。请使用以下模式:
- UI 从外部 API 获取数据
- UI 通过
parent.postMessage({ pluginMessage: ... }, '*')向插件发送数据 - 插件处理数据并将其应用到场景
完整示例请参阅 references/network-and-libraries.md。
常见陷阱
- 缺少
pluginMessage封装 — UI 到插件的消息必须封装为:{ pluginMessage: { ... } }。插件到 UI 的消息无需封装。 - 从插件沙盒中发起请求 — 网络请求只能在 UI 代码中使用。将
fetch调用移至src/。 - 使用
localStorage/sessionStorage— 沙盒 iframe 会阻止浏览器存储 API。请在插件代码中使用creator.clientStorage。 - 不检查节点类型 — 访问特定类型的属性前,务必先验证
node.type。 - 在动画属性上设置
staticValue— 存在关键帧时,设置staticValue不会影响动画。请先清除关键帧,或直接修改关键帧值。 - 形状不可见 — 形状需要填充或描边才能显示。调用
createRectangle()后,请调用createFill()。 - 缩放值是百分比 —
100= 100% 缩放(不是1.0)。正常尺寸请使用{ x: 100, y: 100 }。 - 不透明度范围为 0-100 — 不是 0-1。完全不透明请使用
100。 - 颜色值范围为 0-255 — RGB 通道使用范围
{ r: 0-255, g: 0-255, b: 0-255 }。 - 未尽早调用
creator.ui.show()— 请在plugin.ts顶部、设置消息处理器之前调用。 - UI 就绪前发送消息 — 紧接着
creator.ui.show()调用creator.ui.postMessage()时,消息会被丢弃,因为 iframe 尚未加载。请使用“ui-ready”握手:让 UI 在挂载时发送{ type: 'ui-ready' },然后插件仅在收到该消息后发送数据。 - 假设每个图层都有变换属性 — 音频图层没有
visible、position、scale、rotation或opacity。遍历scene.layers并读取这些成员的代码会编译失败;在包含音频的项目中运行时也会抛出异常。请先按类型筛选,并跳过未处理的类型。
验证清单
任务完成前:
- 运行
npm exec tsc -- -b— 修复所有类型错误 - 测试消息流:UI 发送 > 插件接收 > 插件响应 > UI 接收
- 确认网络请求由 UI 代码发起,而非插件沙盒
- 验证两个方向上的
pluginMessage封装是否正确
参考指南
如需了解更多信息,请按需查阅以下参考文件:
| 参考文件 | 查阅时机 |
|---|---|
references/architecture-and-communication.md | 详细架构、完整的消息传递示例、UI API |
references/scene-graph-and-nodes.md | 场景层级、节点类型、遍历模式 |
references/shapes-styling-animation.md | 创建形状、填充/描边/渐变、关键帧、缓动 |
references/importing-assets.md | LOTTIE/SVG/IMAGE/AUDIO 导入格式和模式 |
references/storage-and-events.md | clientStorage、节点数据、选择事件、时间轴 API |
references/network-and-libraries.md | 从 UI 发起请求的模式、使用 npm 包 |