mcpField
C
creator-plugin-development

creator-plugin-development

github.com/lottiefiles开发152 次安装3 周前更新
代码仓库

关于 creator-plugin-development

面向 LottieFiles Creator 插件开发者的开发技能,说明如何构建动画编辑器插件。内容涵盖操作场景的沙盒与 React UI 分层,以及 API 和消息通信实践。

主要功能

  1. 01沙盒与 UI 架构
  2. 02场景、图层和形状操作
  3. 03关键帧动画模式
  4. 04资源导入指引
  5. 05类型化消息传递
  6. 06开发与验证命令

适用场景

  1. 01搭建 Creator 插件
  2. 02创建或制作场景元素动画
  3. 03导入 SVG、图像或 Lottie 资源
  4. 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.md
importing-assets.md
network-and-libraries.md
scene-graph-and-nodes.md
shapes-styling-animation.md
storage-and-events.md

详细介绍

Creator 插件开发

Creator 插件扩展了 LottieFiles Creator 动画应用。它们采用双部分沙盒架构:

  1. 插件沙盒(plugin/plugin.ts)— 在隔离环境中运行,可访问 creator 全局 API。可以操作场景、图层、形状和关键帧。不能发起网络请求。
  2. UI(src/)— 在 iframe 中渲染的标准 React 应用。可以通过 fetch 发起网络请求。无法访问 creator API。

这两部分仅通过消息传递进行通信。

项目结构

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 请求。请使用以下模式:

  1. UI 从外部 API 获取数据
  2. UI 通过 parent.postMessage({ pluginMessage: ... }, '*') 向插件发送数据
  3. 插件处理数据并将其应用到场景

完整示例请参阅 references/network-and-libraries.md。

常见陷阱

  1. 缺少 pluginMessage 封装 — UI 到插件的消息必须封装为:{ pluginMessage: { ... } }。插件到 UI 的消息无需封装。
  2. 从插件沙盒中发起请求 — 网络请求只能在 UI 代码中使用。将 fetch 调用移至 src/。
  3. 使用 localStorage/sessionStorage — 沙盒 iframe 会阻止浏览器存储 API。请在插件代码中使用 creator.clientStorage。
  4. 不检查节点类型 — 访问特定类型的属性前,务必先验证 node.type。
  5. 在动画属性上设置 staticValue — 存在关键帧时,设置 staticValue 不会影响动画。请先清除关键帧,或直接修改关键帧值。
  6. 形状不可见 — 形状需要填充或描边才能显示。调用 createRectangle() 后,请调用 createFill()。
  7. 缩放值是百分比 — 100 = 100% 缩放(不是 1.0)。正常尺寸请使用 { x: 100, y: 100 }。
  8. 不透明度范围为 0-100 — 不是 0-1。完全不透明请使用 100。
  9. 颜色值范围为 0-255 — RGB 通道使用范围 { r: 0-255, g: 0-255, b: 0-255 }。
  10. 未尽早调用 creator.ui.show() — 请在 plugin.ts 顶部、设置消息处理器之前调用。
  11. UI 就绪前发送消息 — 紧接着 creator.ui.show() 调用 creator.ui.postMessage() 时,消息会被丢弃,因为 iframe 尚未加载。请使用“ui-ready”握手:让 UI 在挂载时发送 { type: 'ui-ready' },然后插件仅在收到该消息后发送数据。
  12. 假设每个图层都有变换属性 — 音频图层没有 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.mdLOTTIE/SVG/IMAGE/AUDIO 导入格式和模式
references/storage-and-events.mdclientStorage、节点数据、选择事件、时间轴 API
references/network-and-libraries.md从 UI 发起请求的模式、使用 npm 包

评论

支持 Markdown