canvas-editor 快速上手:基于 Canvas 的 WYSIWYG 富文本编辑器集成指南

原创2026-10-02 10:52:431,685 阅读
文章标签:前端富文本UI组件

canvas-editor 快速上手:基于 Canvas 的 WYSIWYG 富文本编辑器集成指南

本文是 canvas-editor(@hufe921/canvas-editor)官方文档 Getting Started 的深度实战解读。canvas-editor 是一款基于 Canvas/SVG 自研光标与文本排版内核的 WYSIWYG(所见即所得)富文本编辑器。读完本文,你将掌握从 NPM 安装、容器准备、实例化编辑器(正文/页眉/页脚数据格式)、到配置编辑器与后续扩展(命令 API、右键菜单、插件、国际化)的完整落地路径,可直接照抄示例在你的业务项目中跑通一个可编辑文档。

编辑器概览与能力清单

canvas-editor 的核心卖点是完整自研的光标与文本排版实现,不依赖浏览器原生 contenteditable,因此在不同浏览器环境下的排版表现更可控。底层渲染默认基于 Canvas,同时官方还提供了两个可切换的渲染分支:

  • SVG 渲染分支:见 feature/svg 分支代码;
  • PDF 渲染分支:基于 pdfjs 完整绘制 PDF,见 feature/pdf 分支代码。

⚠️ 官方只发布编辑器核心层的 npm 包,菜单栏、工具栏等外部工具需要参考文档自行扩展,或直接参考官方仓库的实现,详见官方 demo。

从当前仓库源码看,编辑器实例对外暴露了 command(命令系统)、listener(监听器)、eventBus(事件总线)、override(方法重写)、register(右键菜单/快捷键/i18n 注册)、macro(宏管理)、use(插件注册)等能力,见 src/editor/index.ts。

核心特性一览

根据官方文档与 features 文档,编辑器提供如下能力(数字为当前仓库文档口径):

分类 能力说明
富文本操作 撤销/重做、格式刷、清除格式、字体、字号、加粗、斜体、下划线、删除线、上标/下标、字体颜色、高亮、对齐、行高、行距、标题、列表、首行缩进
插入元素 表格、图片、超链接、分割线、分页符、LaTeX 公式、日期选择器、Tab、内容块(iframe/video)、标签、区域
表格 插入/删除行列、合并/拆分单元格、边框样式、背景色、垂直对齐、rowspan/colspan、跨页分页(pagingRepeat 表头回显)
打印 基于 canvas 转图片、PDF 绘制
控件(6 种) 文本、数值、下拉(Select)、单选组、复选组、日期;支持占位符、前后缀、默认值、禁用、缩进对齐规则、控件嵌套
级联表达式 控件值变化时自动联动其他控件/标题的可见性、必填、可编辑、可删除状态,支持计算表达式(compute)
修订留痕(Trace) 记录并可视化标记插入(蓝色下划线)与删除(红色中划线)的修订
宏(Macro) 录制宏与脚本宏,支持命令录制/回放、JSON 序列化、自定义逻辑
编辑器模式(8 种) Edit(编辑)、Clean(清洁)、Readonly(只读)、Form(表单)、Print(打印)、Design(设计)、Graffiti(涂鸦)、Trace(留痕)
页面模式 分页(paging)/连续(continuity),支持页边距、页眉、页脚、页码
水印 文字/图片水印,可置于内容上方或下方
评论 添加、定位、删除文本评论
目录 根据标题自动生成,点击跳转
右键菜单 内置菜单,可按元素类型自定义
快捷键 内置快捷键,支持自定义覆盖与禁用
拖拽 文本、元素、控件
搜索替换 关键词搜索、导航、替换
i18n 内置中英文,可扩展其他语言
插件生态 官方提供 20+ 插件(Word/Excel 导入导出、公式、评论、拼写检查等)

仓库文档统计口径为:19 种元素类型、6 种表单控件、8 种编辑器模式、154+ 命令 API、16+ 事件、20+ 官方插件。

Step 1:安装 NPM 包

在项目中安装核心包:

npm i @hufe921/canvas-editor --save

包名与当前仓库 package.json 中声明的 name: "@hufe921/canvas-editor" 一致,当前仓库版本为 1.0.4,类型声明位于 dist/src/editor/index.d.ts,同时提供 UMD(dist/canvas-editor.umd.cjs)与 ESM(dist/canvas-editor.js)两种产物,可直接在浏览器 <script> 或打包工具中使用。

需要说明的是,当前仓库源码要求的 Node 版本为 >=24.13.1(见 package.json 的 engines 字段),这是构建仓库源码的环境前提;作为 npm 依赖使用时,请以你实际项目环境为准。

Step 2:准备容器 DOM

编辑器会挂载到你指定的 DOM 容器上:

<div class="canvas-editor"></div>

该容器最终由 Draw 类接管并注入编辑器的内部 DOM 结构,见 src/editor/index.ts。容器可以是一个普通 div,编辑器会自动为其构建页面渲染区。

Step 3:实例化编辑器

只含正文的示例

import Editor from '@hufe921/canvas-editor'

new Editor(
  document.querySelector('.canvas-editor'),
  [
    {
      value: 'Hello World'
    }
  ],
  {}
)

构造函数签名为 new Editor(container, data, options),见 src/editor/index.ts:

  • container:挂载容器(HTMLDivElement);
  • data:可以是 IElement[](仅正文)或 IEditorData(含页眉/正文/页脚/涂鸦);
  • options:IEditorOption 配置对象,可省略(默认 {})。

从源码看,当 data 为数组时,会直接作为正文元素列表(mainElementList),并统一经过 formatElementList 做元素格式化与补偿(src/editor/index.ts)。每个元素的最简形态就是一个包含 value 字符串的对象,对应接口 IElementBasic(src/editor/interface/Element.ts),元素可选字段还包括 type(元素类型)、id、extension、externalId、hint 等。

含正文、页眉、页脚的示例

import Editor from '@hufe921/canvas-editor'

new Editor(
  document.querySelector('.canvas-editor'),
  {
    header: [
      {
        value: 'Header',
        rowFlex: RowFlex.CENTER
      }
    ],
    main: [
      {
        value: 'Hello World'
      }
    ],
    footer: [
      {
        value: 'canvas-editor',
        size: 12
      }
    ]
  },
  {}
)

这个示例对应 IEditorData 接口(src/editor/interface/Editor.ts):

interface IEditorData {
  header?: IElement[]   // 页眉元素
  main: IElement[]      // 正文元素(必填)
  footer?: IElement[]   // 页脚元素
  graffiti?: IGraffitiData[] // 涂鸦数据
}

rowFlex 使用编辑器导出的枚举 RowFlex(左对齐/居中/右对齐/两端对齐/分散对齐),源码定义见 src/editor/dataset/enum/Row.ts:

export enum RowFlex {
  LEFT = 'left',
  CENTER = 'center',
  RIGHT = 'right',
  ALIGNMENT = 'alignment',
  JUSTIFY = 'justify'
}

与官方 demo(src/main.ts)中“第一人民医院 / 门诊病历”的页眉示例完全一致——页眉元素支持 size(字号)、rowFlex(行对齐)、type: ElementType.SEPARATOR(插入分割线)等字段,见 src/main.ts。

实例上的可用能力

new Editor(...) 返回的实例除渲染文档外,还挂载了以下核心模块(src/editor/index.ts):

  • instance.command:命令系统,所有 execute* API(如 executeUndo、executeBold、executeInsertTable、executePrint 等);
  • instance.listener:单回调监听;
  • instance.eventBus:发布/订阅事件总线;
  • instance.override:拦截/重写粘贴、复制、拖拽等默认行为;
  • instance.register:注册自定义右键菜单、快捷键与语言包;
  • instance.macro:宏管理;
  • instance.use(plugin):注册插件;
  • instance.version:版本号;
  • instance.destroy():销毁编辑器。

官方 demo 中通过 Reflect.set(window, 'editor', instance) 与 Reflect.set(window, '__CANVAS_EDITOR_INSTANCE__', instance) 将实例挂到全局,分别供 Cypress 测试与 devtools 调试面板使用(src/main.ts)。

Step 4:配置编辑器

第三步中的第三个参数就是配置对象 IEditorOption。官方文档指明“详细配置见下一章节”,即 option.md,这里给出高频配置项的速查(默认值取自文档与 IEditorOption 接口 src/editor/interface/Editor.ts):

interface IEditorOption {
  mode?: EditorMode // 编辑模式:Edit / Clean / Readonly / Form / Print / Design / Graffiti / Trace。默认: Edit
  locale?: string // 语言。默认: zhCN
  defaultType?: string // 默认元素类型。默认: TEXT
  defaultColor?: string // 默认字体颜色。默认: #000000
  defaultFont?: string // 默认字体。默认: Microsoft YaHei
  defaultSize?: number // 默认字号。默认: 16
  minSize?: number // 最小字号。默认: 5
  maxSize?: number // 最大字号。默认: 72
  defaultBasicRowMarginHeight?: number // 默认行高。默认: 8
  defaultRowMargin?: number // 默认行距。默认: 1
  defaultTabWidth?: number // 默认 Tab 宽度。默认: 32
  width?: number // 纸张宽度。默认: 794
  height?: number // 纸张高度。默认: 1123
  scale?: number // 缩放比例。默认: 1
  pageGap?: number // 纸张间距。默认: 20
  margins?: IMargin // 页边距 [top, right, bottom, left]。默认: [100, 120, 100, 120]
  pageMode?: PageMode // 页面模式:Paging(分页)/ Continuity(连续)。默认: Paging
  renderMode?: RenderMode // 渲染模式:speed(多字组合渲染)/ compatibility(逐字渲染,规避浏览器字体等环境差异)。默认: speed
  historyMaxRecordCount?: number // 撤销/重做历史最大记录数。默认: 100
  printPixelRatio?: number // 打印像素比(越大越清晰、体积越大)。默认: 3
  watermark?: IWatermark // 水印配置
  control?: IControlOption // 控件配置
  placeholder?: IPlaceholder // 占位符配置
  // ... 更多见 option.md
}

mode 的可选值在源码枚举 EditorMode 中有完整定义(src/editor/dataset/enum/Editor.ts):edit(可编辑、辅助元素均存在)、clean(隐藏分页符等辅助元素)、readonly(不可编辑)、form(仅控件内可编辑)、print(隐藏辅助元素、选区、未书写控件及边框)、design(不处理不可删除、只读等配置)、graffiti(屏蔽选区、长按绘制线条)、trace(只读查看痕迹)。

一个带配置的完整实例化示例:

import Editor, { EditorMode, PageMode } from '@hufe921/canvas-editor'

const instance = new Editor(
  document.querySelector('.canvas-editor'),
  [
    { value: 'Hello World' }
  ],
  {
    mode: EditorMode.EDIT,
    locale: 'en',
    defaultFont: 'Arial',
    defaultSize: 14,
    pageMode: PageMode.PAGING,
    historyMaxRecordCount: 200,
    watermark: {
      data: 'DRAFT',
      color: '#AEB5C0',
      opacity: 0.3,
      repeat: true
    },
    placeholder: {
      data: '请输入内容...'
    }
  }
)

进一步扩展:实例 API、命令与插件

实例化完成只是开始。官方文档体系建议按以下顺序继续深入:

  • 实例 API:instance.version、instance.destroy() 等,见 api-instance.md。注意 destroy() 只销毁编辑器 DOM 与相关事件,菜单栏、工具栏、外部变量等需要自行清理。
  • 执行类命令:instance.command.executeXxx(),完整列表(154+)见 command-execute.md;获取类命令见 command-get.md。
  • 完整配置:所有配置项(表格、页眉页脚、页码、控件、光标、标题、背景、分栏、留痕、标尺、放大镜等)见 option.md,实验性配置(如 lab.incrementalCompute 行级增量布局)见 lab.md。
  • 事件监听:listener 与 eventBus(contentChange、rangeChange、rangeStyleChange、controlChange 等 16+ 事件)见 listener.md 与 eventbus.md。
  • 右键菜单与快捷键:内置菜单与自定义覆盖见 contextmenu-internal.md、contextmenu-custom.md;快捷键见 shortcut-internal.md、shortcut-custom.md。
  • i18n:内置中文与英文,通过 register.langMap(locale, lang) 扩展其他语言,见 i18n.md。
  • 插件机制:instance.use(plugin) 注册插件,官方 20+ 插件(Word/Excel 导入导出、公式、评论、拼写检查等),自研插件开发见 plugin-custom.md,仓库内的示例插件位于 src/plugins(copy、markdown、spellcheck)。

从源码结构看(src/editor/index.ts),实例化时会依次构建:Command(基于 CommandAdapt 封装 Draw)→ MacroManager → ContextMenu → Shortcut → Register(聚合右键菜单、快捷键、i18n 注册入口,见 src/editor/core/register/Register.ts)→ Plugin(提供 use 注册)。这些模块共同构成了“核心层只做排版渲染、外部能力全部可扩展”的架构。

在仓库中运行与验证

如果你希望直接在当前仓库中体验完整编辑器(包含官方 demo 的菜单栏与病历模板数据 src/mock.ts),可以使用以下命令(环境需满足 node >= 24.13.1):

# 安装依赖
pnpm install

# 启动开发服务器(vite)
npm run dev

# 运行单元测试(vitest)
npm run test:unit

# 运行 E2E 测试(cypress,含编辑、菜单、控件、表格等场景)
npm run cypress:run

# 构建仓库自带的演示应用
npm run build

对应脚本定义见 package.json。仓库的单元测试覆盖了编辑器核心(tests/editor/editor.test.ts)、命令系统(tests/core/command)、事件总线、历史记录、插件、级联表达式等;测试工厂 tests/factories/editor.ts 中通过 new Editor(container, data, options) 创建实例,与本文第三步的用法完全一致。

登录后查看全文
canvas-editor