Elementor Atomic Builder Interactions 交互系统深度解析:从 Schema 校验到 Motion.js 前端运行时
【免费下载链接】elementorThe most advanced frontend drag & drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor
导读:本文围绕 Elementor 开源仓库中 docs/atomic-builder/interactions 文档体系,完整剖析 v4 Atomic(原子)元素上的"交互(Interactions)"能力——包括数据模型、Schema 校验管线、编辑器@elementor/editor-interactions包与 Motion.js 前端运行时。读完你既能掌握设计者视角的动效配置模型,也能以开发者身份理解如何通过 filter 扩展交互 Schema、注册编辑器控件,以及定位"动画不生效"类问题的完整排查链路。
一、Interactions 是什么:分层架构与实验门控
Interactions 为 Atomic(v4)元素提供动效能力:每个元素持有一个interactionsprop——一个带版本号的交互项列表,每个交互项 = 触发器(trigger)+ 动画预设(animation preset)+ 可选的断点排除(breakpoint exclusions)。数据在保存时校验、写入 postmeta 缓存供前端读取,最终由客户端Motion.js执行动画。
从 docs/atomic-builder/interactions/overview.md 的架构表可以看到完整的四层分布:
| 层 | 位置 |
|---|---|
| PHP 模块 | modules/interactions/ |
| 编辑器包 | packages/packages/core/editor-interactions/ |
| 前端脚本 | modules/interactions/assets/js/ |
| 动画库 | Motion.js(assets/lib/motion/,版本 v11.13.5) |
该功能受实验特性门控约束:Module::is_experiment_active()要求启用e_atomic_elements(即AtomicWidgetsModule::EXPERIMENT_NAME,定义于 modules/atomic-widgets/module.php)。若实验未激活,modules/interactions/module.php 的构造函数会直接返回、不注册任何 hook。这也解释了为何在普通模式下看不到 Interactions 标签页。
不同角色的使用场景:
- 设计师:在编辑器 Interactions 标签页配置入场动画与滚动动画;
- 插件/Addon 作者:通过
elementor/atomic-widgets/interactions/schemafilter 扩展 Schema,通过registerInteractionsControl注册编辑器控件; - 内部贡献者:在
modules/interactions/(保存管线、校验、前端)或editor-interactions(标签页 UI、预览)中工作。
二、数据模型:Interaction Item 与文档结构
2.1 Interaction Item
每个交互项是$$type: "interaction-item"的 PropValue,包含四个字段(见 docs/atomic-builder/interactions/schema.md):
| 字段 | Prop 类型 | 描述 |
|---|---|---|
interaction_id | string | 稳定的每项 ID;保存时由 Parser 分配为{post_id}-{element_id}-* |
trigger | string(enum) | 动画何时触发 |
animation | animation-preset-props | 效果、类型、方向、时序、配置 |
breakpoints | interaction-breakpoints | 断点排除(可选) |
2.2 文档形状(Document Shape)
完整的元素交互数据按如下 JSON 结构存储:
{ "version": 1, "items": [ { "$$type": "interaction-item", "value": { "interaction_id": { "$$type": "string", "value": "hero-fade-in" }, "trigger": { "$$type": "string", "value": "scrollIn" }, "animation": { "$$type": "animation-preset-props", "value": { "...": "..." } } } } ] }这套"带类型的 PropValue 包装"({ $$type, value })是 Atomic Widgets 数据模型的基础约定,详细规范可参考 docs/atomic-builder/fundamentals/prop-value.md。编辑器端构建这些结构的工具函数集中在 prop-value-utils.ts:createInteractionItem、createAnimationPreset、createTimingConfig、createConfig等,它们把 TS 侧平铺字段转换为上述嵌套 PropValue 形态。
2.3 动画预设(animation-preset-props)
| 字段 | Prop 类型 | 描述 |
|---|---|---|
effect | string(enum) | fade、slide、scale、custom |
type | string(enum) | in或out |
direction | string(enum) | 滑动方向(见下方快照) |
timing_config | timing-config | duration、delay(Time_Size_Prop_Type,毫秒) |
config | config-v2 | replay、easing、relativeTo、repeat、times、start、end |
custom_effect | custom-effect | keyframes—— 仅 Pro(meta: pro) |
2.4 断点(interaction-breakpoints)
| 字段 | Prop 类型 | 描述 |
|---|---|---|
excluded | excluded-breakpoints | 跳过该交互的断点标签列表 |
三、Schema:服务端 Prop 类型树与内置预设
3.1 Interactions_Schema
Interactions_Schema是交互数据的权威 PHP prop-type 树(modules/interactions/schema/interactions-schema.php),被校验、导入导出、prop-type 迁移以及编辑器 MCP schema 资源共同消费:
Interactions_Schema::get(); // → apply_filters( 'elementor/atomic-widgets/interactions/schema', … )内置 schema 结构为version(当前为 1)+items数组,元素类型由Interaction_Item_Prop_Type定义。对应的各 Prop 类型类位于 modules/interactions/props/,全部继承 Atomic Widgets 的Object_Prop_Type:
| 符号 | key | 职责 | 源码 |
|---|---|---|---|
Interaction_Item_Prop_Type | interaction-item | 根条目形状 | props/interaction-item-prop-type.php |
Animation_Preset_Prop_Type | animation-preset-props | 效果 + 时序形状 | props/animation-preset-prop-type.php |
Animation_Config_Prop_Type | config-v2 | replay、easing、滚动区间 | props/animation-config-prop-type.php |
Timing_Config_Prop_Type | timing-config | duration + delay | props/timing-config-prop-type.php |
Custom_Effect_Prop_Type | custom-effect | 关键帧自定义效果 | props/custom-effect-prop-type.php |
Interaction_Breakpoints_Prop_Type | interaction-breakpoints | 断点包装 | props/interaction-breakpoints-prop-type.php |
Presets | — | 枚举常量与默认值 | presets.php |
3.2 内置值快照(Built-in Values)
以下枚举与默认值直接来自 modules/interactions/presets.php,其中标注pro的选项仅在 Pro 中开放:
触发器(Triggers)
| Key | 层级 |
|---|---|
load、scrollIn | Base |
scrollOut、scrollOn、hover、click | Pro |
效果(Effects):fade、slide、scale(Base);custom(Pro)。
类型(Types):in、out。
方向(Directions):left、right、top、bottom、top-left、top-right、bottom-left、bottom-right、""。
缓动(Easing):easeIn(Base);easeOut、easeInOut、backIn、backInOut、backOut、linear(Pro)。
默认值:duration600ms、delay0、slide distance100、scale start0、easingeaseIn、滚动区间85%–15%、relativeTo为viewport、repeat 为''(可选loop/times)。
上限:每个元素最多 5 个交互项(由Validation强制)。
这些默认值通过Presets::defaults()暴露,并在 module.php 的get_config()中以constants键注入ElementorInteractionsConfig全局对象,供编辑器与前端读取。例如Animation_Config_Prop_Type中start/end是单位为%的Size_Prop_Type(默认 85/15),easing的默认值easeIn与Presets::DEFAULT_EASING一致(见 props/animation-config-prop-type.php)。
3.3 Pro 门控机制
Prop 类型通过meta( 'pro', … )标记 Pro-only 值,例如Animation_Preset_Prop_Type中的custom_effect、Animation_Config_Prop_Type中的replay/relativeTo/repeat/times/start/end;Interaction_Item_Prop_Type的trigger通过Presets::ADDITIONAL_TRIGGERS标记 Pro 触发器。编辑器侧则用PromotionSelect限制基础档位可选值,参考 editor-interactions/src/ui/ 下的promotion-select.tsx与interactions-promotion-chip.tsx。
四、保存管线:校验、ID 分配与 postmeta 缓存
4.1 两步保存流程
Module::register_hooks()(modules/interactions/module.php)挂载了两个关键钩子:
elementor/document/save/data→handle_interactions():先Validation->sanitize()递归消毒并校验,再Validation->validate()检查数量上限,最后Parser->assign_interaction_ids()分配稳定 ID;elementor/document/after_save→handle_interactions_cache():调用Interactions_Postmeta->process_content()写入按元素分组的缓存。
4.2 Validation:逐字段校验
modules/interactions/validation.php 定义白名单:VALID_EFFECTS = ['fade','slide','scale','custom']、VALID_TYPES = ['in','out']、VALID_DIRECTIONS(含空串)与VALID_REPEAT_MODES = ['','loop','times']。sanitize()递归遍历元素树(含嵌套elements),对每个interactions字段做以下处理:
- 兼容数组与 JSON 字符串两种输入,统一解码出
items; - 校验每项的
$$type === 'interaction-item',trigger必须通过TriggerValueValidator(validators/trigger-value.php 中的六种合法值); - 校验
animation:effect/type/direction枚举、timing_config(duration/delay同时接受number与size两种格式,且>= 0)、config(replay必须为 boolean、repeat必须是loop|times|''、times >= 1、start/end在 0–100 之间)、custom_effect通过Custom_Effect_Value校验; breakpoints若存在则必须通过BreakpointsValueValidator。
validate()阶段对每个元素累计交互项数量,超过$max_number_of_interactions = 5时抛出异常Element %1$s has more than %2$d interactions,从而在保存层面阻断非法数据。对应的单元测试覆盖见 tests/phpunit/elementor/modules/interactions/test-validation.php。
4.3 Parser:稳定 ID 分配
modules/interactions/parser.php 负责给交互项分配跨会话稳定的interaction_id:
- 未携带 ID 或携带
temp-前缀临时 ID(编辑器新建项时由generateTempInteractionId生成)的项,会通过Utils::generate_id( "{$post_id}-{$element_id}-", $ids_lookup )重新生成; - 已有稳定 ID 的项保留,并登记进
ids_lookup防止重复。
因此保存后形如123-abc123-1的 ID 会成为该交互项的永久标识,前端 DOM 绑定依赖的是元素 ID(见第六节),而interaction_id更多服务于去重、复制粘贴与 MCP 工具定位。Parser的行为由 tests/phpunit/elementor/modules/interactions/test-parser.php 验证。
4.4 Postmeta 缓存
缓存由 modules/interactions/cache/interactions-postmeta.php 管理,Meta key 为elementor-interactions-cache:
- 写:
elementor/document/after_save→process_content()(自动跳过 autosave 与草稿状态); - 读:
load_content()直接读 postmeta,缓存过期时回退process_content()重建; - 删:当提取出的交互映射为空时
delete_post_meta()。
真正做树遍历提取的是 cache/elements-interactions.php:递归遍历元素树,提取每个元素的interactions.items,生成element_id => items映射。这层缓存让前端渲染时无需每次重新解析整个元素树,是文档中强调的性能关键点。
五、编辑器端:@elementor/editor-interactions包
5.1 包结构与初始化
@elementor/editor-interactions实现了 v4 编辑器侧的完整交互表面:Interactions 标签页、逐字段控件、预览播放、剪贴板粘贴与 MCP 工具。它作为 atomic-widgets v2 包注册,入口为 src/init.ts,init()依次完成:
- 注册默认数据提供者
documentElementsInteractionsProvider; - 注册粘贴命令与"重复元素时清理临时交互 ID"的 hook;
- 注册基础控件(见下表);
- 注册
interactionsMCP 域(initMcpInteractions,参考 docs/atomic-builder/mcp/registering-editor-tools.md)。
5.2 Interactions 标签页
InteractionsTab(src/components/interactions-tab.tsx)的工作流程:
- 通过
useElementInteractions( elementId )读取元素交互数据; - 有数据时在
InteractionsProvider内渲染InteractionsList,无数据时展示EmptyState(点击创建首个交互); - 逐项编辑通过
InteractionDetails/InteractionSettings完成。
useElementInteractions(src/hooks/use-element-interactions.ts)订阅elementor/element/update_interactions窗口事件,保证外部改动实时同步到标签页。编辑器内预览播放复用的是editor-interactions.js(Motion.js 运行时),而非 React 包本身。
5.3 控件注册表(Controls Registry)
核心 API 定义于 src/interactions-controls-registry.ts:
registerInteractionsControl( { type, component, options? } ); getInteractionsControl( type ); getInteractionsControlOptions( type );InteractionsControlType覆盖 14 种控件:trigger、effect、effectType、direction、duration、delay、replay、repeat、times、easing、relativeTo、start、end、customEffects。其中基础档在init.ts中注册的选项如下:
| 控件类型 | 基础选项 |
|---|---|
trigger | load、scrollIn |
effect | fade、slide、scale |
effectType | in、out |
direction | top、bottom、left、right |
easing | easeIn |
replay | no |
repeat | (无固定选项) |
duration与delay通过TimeFrameIndicator内联渲染;relativeTo、start、end、times、customEffects等注册槽位已存在,但由配套包(如 Pro)在各自init()时注册——这正是"注册表 + 槽位"设计支持 Pro 扩展的方式。
5.4 数据提供者与配置桥
interactionsRepository(src/interactions-repository.ts)是提供者注册表:默认提供者documentElementsInteractionsProvider读取文档元素上的交互数据;任何编辑器包都可以在自己的init()中通过interactionsRepository.register( createInteractionsProvider( … ) )注册新提供者(createInteractionsProvider支持key、priority、subscribe与actions,见 src/utils/create-interactions-provider.ts)。
配置桥get-interactions-config.ts读取window.ElementorInteractionsConfig(由 PHPModule::enqueue_editor_scripts通过wp_add_inline_script注入,见 module.php),使前端获得与 PHP 侧一致的默认常量与活动断点。
六、前端运行时:Motion.js 渲染管线
6.1 完整管线
modules/interactions/interactions-frontend-handler.php 与 assets/js/interactions.js 构成前端管线:
elementor/frontend/builder_content_data → collect_document_interactions → Interactions_Postmeta::load_content(或 process_content 回退) → Interactions_Collector::register 按元素注册 wp_footer(优先级 1) → print_interactions_data → <script id="elementor-interactions-data">…</script> interactions.js(DOMContentLoaded) → 解析 JSON → 查询 [data-interaction-id] → Motion.animate / inView编辑模式下整个流程被跳过(collect_document_interactions与print_interactions_data均在开头检查Plugin::$instance->editor->is_edit_mode())。
6.2 Footer JSON 形状
print_interactions_data()将Interactions_Collector聚合的数据编码为 JSON 输出到页脚脚本标签:
{ "elementId": "abc123", "dataId": "abc123", "interactions": [ /* items */ ] }脚本标签 id 为elementor-interactions-data(对应Module::SCRIPT_ID_INTERACTIONS_DATA)。interactions.js找到该标签后JSON.parse,再对每条记录执行document.querySelectorAll( '[data-interaction-id="' + elementId + '"]' )进行 DOM 绑定。
6.3 运行时触发器行为
assets/js/interactions.js 中的三种运行时触发器行为:
| 触发器 | 行为 |
|---|---|
load | 立即animate()(defaultAnimation) |
scrollIn | inView且amount: 0,进入视口时播放 |
scrollOut | inView且amount: 0.85,退出视口时播放(先以 0 时长复位到初始关键帧) |
其余 Schema 中的触发器(hover、click、scrollOn)与custom效果会被isSupportedInteraction()直接跳过(见 assets/js/interactions-utils.js)——这正是文档强调的"运行时子集 vs 完整 Schema"差异。若replay为 false,scrollIn/scrollOut播放一次后即停止监听。
6.4 断点排除逻辑
assets/js/interactions-breakpoints.js 从ElementorInteractionsConfig.breakpoints读取活动断点配置(min/max 方向与阈值,由 PHP 侧Module::get_active_breakpoints()注入),监听resize(100ms 防抖)维护当前断点。skipInteraction()(interactions-shared-utils.js)检查交互项的breakpoints.excluded是否包含当前活动断点标签,命中则跳过该动画——实现了响应式场景下"移动端不做入场动画"这类需求。
6.5 关键帧构建与 transform 保留
getKeyframes( effect, type, direction )负责把 Schema 语义转换为 Motion.js 关键帧:
fade→opacity: [0,1](in)或[1,0](out);scale→scale: [scaleStart, 1](in)或[1, scaleStart](out);slide+ 方向 → 按slideDistance(默认 100)生成x/y位移,复合方向(如top-left)会叠加两个轴。
为避免动画覆盖元素已有的 CSS transform(旋转、平移、缩放),运行时先通过getTransformBaselineFromComputedStyle解析getComputedStyle的 matrix,再用preserveTransformKeyframes把基线合并进关键帧。同时applyAnimation会先将element.style.transition置为'none',动画结束后再恢复,防止 CSS transition 干扰 Motion 动画。这些工具函数同时通过window.elementorModules.interactions暴露,供 Pro 等第三方消费。
七、扩展指南:Addon 作者如何接入
7.1 通过 filter 扩展 Schema
Interactions_Schema::get()最终经过elementor/atomic-widgets/interactions/schemafilter,因此 Addon 可以在不修改核心代码的前提下追加字段:
add_filter( 'elementor/atomic-widgets/interactions/schema', function ( array $schema ) { $item = $schema['items'][0]; $shape = $item->get_shape(); $shape['my_extension'] = My_Extension_Prop_Type::make(); $item->set_shape( $shape ); return $schema; } );新的 Prop 类型类应放在modules/interactions/props/同构位置,遵循现有Object_Prop_Type子类写法。
7.2 三端平行修改要求
文档明确强调:新增 trigger / effect 需要平行修改三处,缺一不可:
- PHP 侧:
Validation(枚举白名单)与Presets(选项枚举); - 编辑器侧:
registerInteractionsControl注册对应控件组件; - 前端侧:
isSupportedInteraction()(interactions-utils.js)中的支持列表。
编辑器侧扩展新控件的内部路径为:实现组件 → 在init.ts中registerInteractionsControl;注册新数据提供者 →interactionsRepository.register( createInteractionsProvider( … ) );注册 MCP 工具 →initMcpInteractions( getMCPByDomain( 'interactions', … ) )。前端没有公开的注册 hook,运行时能力的扩展只能修改核心 JS 文件本身。
导入导出模块则通过Interactions_Schema::get()解析/重组交互项,确保 Schema 扩展后导入导出仍能正确往返。
八、测试与验证
仓库为 Interactions 提供了 PHPUnit 与前端 Jest 双层测试保障:
- tests/phpunit/elementor/modules/interactions/test-validation.php:构造标准
interaction-itemPropValue(create_prop_type_interaction与create_config_prop辅助函数),覆盖 effect/type/direction/timing/config 各字段的合法与非法输入; - tests/phpunit/elementor/modules/interactions/test-parser.php:通过
Parser_Ex子类接管 ID 生成,验证{post_id}-{element_id}-{n}分配逻辑; - tests/phpunit/elementor/modules/interactions/test-interactions-collector.php 与 cache/test-elements-interactions.php:验证收集器与树遍历提取;
- 编辑器包测试:src/tests/ 与 src/components/controls/tests/(含
interaction-details、resolve-direction、paste-interactions、各控件组件测试)。
九、常见排查路径与总结
调试"动画未生效"时,按文档给出的链路自底向上排查:
- 实验特性
e_atomic_elements是否开启(未开启则整个模块不加载); - 保存是否成功:检查
Validation是否抛"超过 5 个交互"异常、interaction_id是否已从temp-转为稳定 ID; - 前端数据是否到位:页脚是否存在
#elementor-interactions-data脚本标签、JSON 中elementId是否与 DOM 上data-interaction-id匹配; - 运行时是否跳过:触发器是否落在
load/scrollIn/scrollOut子集内、效果是否为custom、当前断点是否在breakpoints.excluded中; - Motion.js 是否加载成功(
window.Motion.animate/window.Motion.inView是否存在,waitForAnimateFunction最多轮询 10 次)。
Interactions 将"触发器 + 动画预设 + 断点规则"的声明式模型与 Motion.js 的高性能运行时结合,配合 Schema filter、控件注册表与 postmeta 缓存,兼顾了编辑体验、扩展性与前端性能。更完整的模型背景可继续阅读 docs/atomic-builder/atomic-widgets/overview.md、docs/atomic-builder/fundamentals/prop-types.md 与 docs/atomic-builder/migration/prop-type-migrations.md。
【免费下载链接】elementorThe most advanced frontend drag & drop page builder. Create high-end, pixel perfect websites at record speeds. Any theme, any page, any design.项目地址: https://gitcode.com/GitHub_Trending/el/elementor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考