@cgon/leafer-x-studio-displays
v0.1.21
Published
独立的 LeaferJS Display 类型与文档渲染器
Readme
@cgon/leafer-x-studio-displays
与框架无关的 LeaferJS Display 类,使用原生注册标签。运行时不引入 Vue、不创建 Editor,也不依赖 App.tree。
0.1.6 更新
- 域切换由
DomainDisplay统一提交 LeaferJS 布局更新,同一选项下的全部 Display 会同时显示或隐藏。 - 分支切换只使用 LeaferJS 节点、布局和渲染调度,不直接操作 Canvas,也不改写子元素的持久化外观。
原生 JSON
只需导入本包,即可注册 StudioDocumentDisplay、StudioFrameDisplay、全部 Studio*Display 类,以及这些 Display 所需的所有渲染效果。使用方无需安装或导入 Studio 的路径、路径文字或羽化插件。场景直接通过 LeaferJS 自身完成持久化:
import { UI } from '@leafer-ui/core'
import '@cgon/leafer-x-studio-displays'
const json = documentDisplay.toJSON()
const restored = UI.one(json)
leafer.add(restored)节点的 tag 是唯一的运行时类型标识。image、rect、text 等编辑器面板键不会作为节点类型导出。自定义源属性直接注册在对应的 Display 上,并以平铺 JSON 形式序列化,不存在 displayMeta、frameMeta 或 documentMeta 外层结构。
渲染模型文档
import { Leafer } from 'leafer-ui'
import { configureDisplayFonts, loadStudioDocument } from '@cgon/leafer-x-studio-displays'
configureDisplayFonts({
resolveFamily: requested => requested || 'Inter, sans-serif',
prepare: async model => {
await document.fonts.load(`${model.fontWeight} 16px ${model.fontFamily}`, model.text)
},
})
const leafer = new Leafer({ view: document.querySelector('#app') })
const runtime = await loadStudioDocument(leafer, studioDocument)
runtime.setDomainOption('domain-id', 'option-id')
runtime.destroy()
leafer.destroy()loadStudioDocument() 是兼容旧快照的模型文档桥接方法。它会在创建节点前等待配置的文字字体准备完成。新的可移植输出应使用上文的原生 toJSON() 格式。
FrameDisplay 支持分别设置 frameAutoWidth 和 frameAutoHeight。原生 JSON 同时保留自适应规则和当前解析出的 width / height,因此独立 LeaferJS 项目可以立即按导出尺寸渲染,并在 Frame 内容变化后继续自适应。使用 createFramePaintBackground() 可创建跟随 Frame 的颜料或滤镜背景,该背景不参与自适应内容边界计算。
接口响应 JSON 属于运行时数据。原生导出会保留 Frame 的请求配置和 fieldBinding,但不会导出 apiStructure.json。加载导出的场景后,由接入方请求数据,并通过 Frame 的公开方法写入响应:
import { FrameDisplay, refreshRepeatDisplays } from '@cgon/leafer-x-studio-displays'
const frame = restored.children[0] as FrameDisplay
frame.setApiJson(response)
refreshRepeatDisplays(frame)文本和图片绑定会读取 setApiJson() 写入的数据。该方法既可接收 JSON 字符串,也可接收兼容 JSON 的值;之后调用 frame.toJSON() 时,响应数据仍不会被导出。
约定
- 每种 Display 都由各自的模型模板创建。
DisplayModel在内部通过type区分;文本、图片和域内容不会共用错误字段。createDisplayModel、updateDisplayModel和cloneDisplayModel使用与平铺注册节点数据相同的结构,同时兼容旧输入。 - 默认图片以真实 data URL 存储在
imageUrl,图片颜料中则存储在image.url。绑定图片从所在 Frame 的运行时接口 JSON 读取路径;空值或错误时使用fieldBinding.image.fallbackUrl。加载导出 JSON 后,由接入方调用FrameDisplay.setApiJson()恢复运行时数据。 - Leafer 相关包必须使用
package.json中声明的兼容 peer 版本,当前验证基线为2.2.9。 - 路径几何、路径文字、羽化和布尔几何都是本包的内部实现细节,使用方无需额外导入或配置。
- 图片与形状共用羽化滤镜。
featherEffects: [{ id: 'feather-1', enabled: true, radius: 30 }, { id: 'feather-2', enabled: true, radius: 12 }]支持逐层羽化,可与图片调色叠加。关闭或半径为 0 的层跳过。叠加计算由独立 feather 包统一负责,创建、更新及原生 JSON 恢复使用同一滤镜管线;原生滤镜支持filter: { type: 'feather', radius: [30, 12] }。旧feather单层配置仍可读取,显式featherEffects: []表示移除全部羽化。 - 路径文字使用已序列化的
textPathLayout,恢复时不会重复执行编辑器中的邻近路径搜索。 - Display 优先使用 JSON 中记录且当前设备已安装的字体;缺失时依次降级到 Inter、PingFang SC、Microsoft YaHei 和浏览器默认无衬线字体。本包不内置字体文件,也不请求本地字体枚举权限。
- 图片 URL 必须能被使用方访问。支持持久化 HTTP URL 和 data URL,不可移植仅在当前会话有效的 blob URL。
- 主题默认值只影响新创建内容,文档中已保存的颜色仍属于文档数据。
- 辅助线、选中状态、Editor 手柄、悬停状态、缩略地图和视口缩放都不属于渲染文档。
serializeStudioDocument() 仅用于规范化由 loadStudioDocument() 消费的旧模型文档格式。版本 1 输入会通过已注册的 Display 默认值完成规范化;旧文档中原本缺失的信息无法推断。
纯渲染使用方式参见 @cgon/leafer-x-studio-draw 和 examples/standalone-renderer,它们只使用一个 Leafer 根节点。
DomainDisplay
domainOptions 中的普通分支按 key 与绑定值比较;数字、布尔值会转为字符串比较。isElse: true 表示“意外”分支,不比较 key,仅在所有普通分支均未命中时显示。每个域最多添加一个意外分支,并排列在普通分支之后。未命中且没有意外分支时,隐藏所有条件分支。旧文档的 isDefault 默认分支仍兼容;编辑器添加显式意外分支后,原默认分支转为按值判断的普通分支。
isElse 随 domainOptions 保存到原生 JSON。编辑器、独立 Displays 和 Draw 使用同一套运行时判断;数据更新时切换整个分支的所有子节点,遍历中的域按各项数据独立判断。
域的 fieldBinding 只用于条件判断,不改变子节点的数据作用域。子节点选择“父容器”时,穿透域和普通组合,读取最近的遍历项、组件实例或 Frame 数据;遍历项原始字段与 $index、$key、$isFirst、$isLast 同时可用。
BoxDisplay
编辑器中将 StudioBoxDisplay 显示为“盒子”。宽度和高度可以分别根据内容自适应。模型字段 boxAutoWidth / boxAutoHeight 直接映射到 Leafer 原生的 width: null / height: null;固定轴继续保留数值尺寸。原生 JSON 无需任何编辑器或框架逻辑即可保留此尺寸规则。流式布局、间距、内边距、嵌套内容和裁剪仍使用原生 Box 行为。
RepeatDisplay
StudioRepeatDisplay 是组类 Display,内部包含一棵共享的编辑子树,以及真实的 StudioRepeatItemDisplay 数据作用域。原生 JSON 会包含渲染后的各项子树;UI.one(json) 可以直接恢复它们,包括嵌套遍历和各自的局部变换。
平铺配置包括 fieldBinding、repeatAxis(horizontal / vertical)、repeatReverse、repeatWrapCount(0 表示不折行)、repeatWrapReverse、repeatGapX 和 repeatGapY。视觉反向不会改变数据顺序。横向遍历的正常折行方向向下,纵向遍历的正常折行方向向右;反向折行分别向上或向左。每一项根据自身实际渲染边界推进,因此间距为 0 时相邻边缘会紧贴。每一行使用该行自己的最大高度,每一列使用该列自己的最大宽度;较短内容不会继承整个遍历中最大项在主轴上的空白。
对象按照 Object.keys() 顺序遍历,并直接向下提供每个 value。数组、字符串和支持的类数组值按索引提供每一项。$index、$key、$isFirst 和 $isLast 是独立的绑定上下文变量。不支持的标量数据不会生成项目。空集合在编辑器中保留可操作背景;进入原生 Group 后可以编辑共享模板。setEditorState() 用于启用这些临时编辑辅助状态。这些状态不会序列化:预览、图片导出和原生 JSON 导入都不会渲染区域背景,空集合也不会渲染模板内容。
以代码创建时,可使用 RepeatDisplay.fromModel(createRepeatModel(...)) 创建容器。repeat.add(display) 会把内容插入当前正在编辑的项目。批量修改内容或接口数据后,调用 repeat.refresh() 或 refreshRepeatDisplays(document),即可解析绑定、同步其他项目,并按照各项当前内容边界重新布局。以上 API 全部位于 displays 包内,不依赖 Vue 或 Editor。
