npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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。