For the complete documentation index, see llms.txt. This page is also available as Markdown.

Leafer中接入Quill富文本并扩展自定义格式

在画布编辑器里做文本功能,普通 Text 往往只能满足基础输入。一旦要支持局部字号、局部字体、行内描边、阴影、列表、对齐、HTML 内容保存,就需要一个真正的富文本编辑内核。

这个方案的核心思路是:

  • 画布层继续由 Leafer UI 负责渲染和选中。

  • 文本编辑状态下挂载一个 DOM 富文本编辑器。

  • Quill 负责选区、输入、格式化。

  • 编辑完成后把 HTML 和样式同步回画布节点。

  • 需要的特殊样式通过 Quill 自定义 format 扩展。

为什么不用默认 format

Quill 默认支持 bolditalicsizecolor 等能力,但是在画布编辑器里会遇到两个问题:

  1. 默认字号有白名单,不能很好适配设计器里任意 px 的字号。

  2. 一些设计器属性不是 Quill 默认格式,比如 fontWeighttextStroketextShadow、局部字间距。

所以需要在初始化时注册一批自定义格式。

import Quill from "quill";

export const registerQuillCustomFormats = (): void => {
  const SizeStyle = Quill.import("attributors/style/size") as {
    scope: unknown;
    whitelist?: string[] | null;
    constructor: new (
      attrName: string,
      keyName: string,
      options: { scope: unknown },
    ) => unknown;
  };

  // 解除默认字号白名单,设计器里才能使用任意 px 字号
  SizeStyle.whitelist = null;
  Quill.register({ "attributors/style/size": SizeStyle, "formats/size": SizeStyle }, true);

  const StyleAttributorCtor = SizeStyle.constructor;

  const FontWeightAttributor = new StyleAttributorCtor("fontWeight", "font-weight", {
    scope: SizeStyle.scope,
  });

  Quill.register({
    "attributors/style/fontWeight": FontWeightAttributor,
    "formats/fontWeight": FontWeightAttributor,
  }, true);

  const TextStrokeAttributor = new StyleAttributorCtor("textStroke", "-webkit-text-stroke", {
    scope: SizeStyle.scope,
  });

  Quill.register({
    "attributors/style/textStroke": TextStrokeAttributor,
    "formats/textStroke": TextStrokeAttributor,
  }, true);

  const TextShadowAttributor = new StyleAttributorCtor("textShadow", "text-shadow", {
    scope: SizeStyle.scope,
  });

  Quill.register({
    "attributors/style/textShadow": TextShadowAttributor,
    "formats/textShadow": TextShadowAttributor,
  }, true);
};

全局属性和局部属性要分开

富文本里一个很容易踩坑的点是:全局字号、局部字号如果都写到 font-size,后面就很难区分到底是整段文本的属性,还是选区里的属性。

我的处理方式是单独注册局部 format,例如 inlineFontSizeinlineFontFamilyinlineLetterSpacing,这样可以避免全局属性和行内属性互相覆盖。

统一入口处理格式化

在实际业务里,不建议每个按钮都直接操作 Quill。可以封装一个统一的 setHTMLText 方法:

这样做有几个好处:

  • 按钮、快捷键、右侧属性面板都能走同一套逻辑。

  • 授权检测、选区处理、画布节点校验可以集中处理。

  • 后续加新属性,只需要扩展 FORMAT_HANDLERS

使用建议

如果是普通富文本页面,直接使用 Quill 默认能力就够了。但如果是画布编辑器,建议一开始就把“全局属性”和“局部属性”拆清楚。

尤其是字号、字体、字间距、描边这类样式,一旦混在一起,后面做缩放、复制、撤销重做、导入导出时都会变得很难维护。