towxml-canvas-renderer
v1.0.3
Published
微信小程序 Markdown 渲染器,将 Markdown 内容渲染到 Canvas
Maintainers
Readme
Markdown 渲染器架构说明
概述
本模块使用策略模式 + 工厂模式 + 模板方法模式重构,将原本的单体文件(985行)拆分为多个职责明确的小文件,提高了代码的可维护性和可扩展性。
重要说明:本项目基于 towxml 包,接收
towxml()方法解析 Markdown 后返回的节点树数据(TowxmlNode),并将其渲染到 Canvas 上。使用前需要先使用towxml将 Markdown 文本解析为节点树。关于
towxml的使用方法,请参考官方文档:towxml 3.0 如何使用
目录结构
markdown-renderer/
├── index.ts # 主入口,导出 MarkdownRenderer 和类型
├── types.ts # 类型定义(TowxmlNode, RenderStyles, Layout, RenderResult, RenderContext)
├── config.ts # 默认样式配置
├── MarkdownRenderer.ts # 主渲染器类(协调各个渲染器)
├── renderers/ # 渲染器目录
│ ├── index.ts # 渲染器工厂(RenderFactory)
│ ├── base/
│ │ └── BaseRenderer.ts # 基础渲染器抽象类(模板方法模式)
│ ├── text/
│ │ └── TextRenderer.ts # 文本渲染器
│ ├── heading/
│ │ └── HeadingRenderer.ts # 标题渲染器(h1-h6)
│ ├── paragraph/
│ │ └── ParagraphRenderer.ts # 段落渲染器
│ ├── code/
│ │ └── CodeRenderer.ts # 代码块渲染器(行内/块级)
│ ├── strong/
│ │ └── StrongRenderer.ts # 粗体渲染器(支持粗体行内代码)
│ ├── emphasis/
│ │ └── EmphasisRenderer.ts # 斜体渲染器
│ ├── blockquote/
│ │ └── BlockquoteRenderer.ts # 引用块渲染器
│ ├── list/
│ │ └── ListRenderer.ts # 列表渲染器(ul/ol/li)
│ ├── table/
│ │ └── TableRenderer.ts # 表格渲染器
│ └── hr/
│ └── HrRenderer.ts # 分割线渲染器
├── utils/ # 工具类目录
│ ├── TextUtils.ts # 文本处理工具(换行、计算高度、提取文本)
│ ├── CanvasUtils.ts # Canvas 绘制工具(圆角矩形等)
│ └── StateManager.ts # 状态管理(保存/恢复 Canvas 状态)
└── helpers/ # 辅助类目录
└── NodeMatcher.ts # 节点匹配逻辑(判断节点类型)
设计模式说明
1. 策略模式(Strategy Pattern)
- 每个节点类型对应一个独立的渲染器策略类
- 所有渲染器继承自
BaseRenderer - 通过
canHandle()方法判断是否可以处理该节点
2. 工厂模式(Factory Pattern)
RenderFactory负责创建和管理所有渲染器实例- 根据节点类型自动选择合适的渲染器
- 支持特殊规则优先匹配(如 view 标签的 class 判断)
3. 模板方法模式(Template Method Pattern)
BaseRenderer定义渲染流程骨架:- 保存状态 (
saveState) - 执行具体渲染 (
renderInternal- 子类实现) - 恢复状态 (
restoreState)
- 保存状态 (
4. 依赖注入
RenderContext包含所有渲染器需要的共享状态和工具方法- 避免在各个渲染器中重复初始化
使用方式
前置准备
在使用本渲染器之前,需要先使用 towxml 将 Markdown 文本解析为节点树。
在微信小程序中使用 towxml
根据 towxml 官方文档,需要在 app.js 中引入 towxml:
// app.js
App({
// 引入 towxml 3.0 解析方法
towxml: require('/towxml/index')
})然后在页面中使用:
// pages/index/index.js
const app = getApp()
// 使用 towxml 解析 Markdown
const articleData = app.towxml('# Hello World\n\n这是一段 Markdown 内容', 'markdown', {
base: 'https://xxx.com', // 相对资源的 base 路径(可选)
theme: 'light', // 主题:'light' 或 'dark'(可选,默认 'light')
events: { // 绑定事件(可选)
tap: (e) => {
console.log('tap', e)
}
}
})towxml API 说明
app.towxml(str, type, options) 参数:
str[必选] - HTML 或 Markdown 字符串type[必选] - 需要解析的内容类型:'html'或'markdown'options[可选] - 配置选项对象base[string] - 用于指定静态相对资源的 base 路径,例如:'https://xx.com/static/'theme[string] - 主题设置,默认:'light',可选:'light'、'dark'或其它自定义主题events[object] - 用于为元素绑定事件,key为事件名称,value必须为一个函数
基本使用
import { MarkdownRenderer } from 'towxml-canvas-renderer'
const app = getApp()
// 1. 使用 towxml 解析 Markdown
const markdownText = '# 标题\n\n这是一段内容'
const articleData = app.towxml(markdownText, 'markdown')
// 2. 创建渲染器并渲染
const ctx = canvas.getContext('2d')
const renderer = new MarkdownRenderer(ctx, canvasWidth, canvasHeight, {
// 可选的自定义样式
})
const result = renderer.render(articleData)扩展新节点类型
- 在
renderers/下创建新的渲染器目录 - 实现继承自
BaseRenderer的渲染器类 - 在
RenderFactory中注册新渲染器
示例:
export class CustomRenderer extends BaseRenderer {
canHandle(node: TowxmlNode): boolean {
return node.tag === 'custom'
}
protected renderInternal(node: TowxmlNode, layout: Layout): Layout {
// 实现渲染逻辑
return layout
}
}优势
- 职责分离:每个渲染器只负责一种节点类型
- 易于扩展:新增节点类型只需新增一个渲染器类
- 易于测试:可以单独测试每个渲染器
- 易于维护:修改某种节点的渲染逻辑不会影响其他节点
- 代码复用:工具类可在多个渲染器中复用
在微信小程序中使用
安装
npm install towxml-canvas-renderer
# 或使用 pnpm
pnpm add towxml-canvas-renderer构建 npm
- 打开微信开发者工具
- 点击菜单栏:工具 -> 构建 npm
- 构建完成后,
node_modules目录下会生成miniprogram_npm目录
引入使用
完整示例:
// pages/index/index.js
import { MarkdownRenderer, defaultStyles } from 'towxml-canvas-renderer'
const app = getApp()
Page({
data: {
canvasWidth: 750,
canvasHeight: 1000
},
onReady() {
// 1. 使用 towxml 解析 Markdown 文本
const markdownText = `# 标题\n\n这是一段 **Markdown** 内容`
const articleData = app.towxml(markdownText, 'markdown', {
base: 'https://your-domain.com', // 可选:静态资源 base 路径
theme: 'light' // 可选:主题设置
})
// 2. 获取 Canvas 上下文
const query = wx.createSelectorQuery()
query.select('#canvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
// 设置 Canvas 尺寸
const dpr = wx.getSystemInfoSync().pixelRatio
canvas.width = this.data.canvasWidth * dpr
canvas.height = this.data.canvasHeight * dpr
ctx.scale(dpr, dpr)
// 3. 创建渲染器实例
const renderer = new MarkdownRenderer(
ctx,
this.data.canvasWidth,
this.data.canvasHeight,
{
// 可选:自定义样式配置
padding: 40,
baseFontSize: 28,
colors: {
text: '#333333',
// ... 其他样式配置
}
}
)
// 4. 渲染 Markdown 内容(articleData 是 towxml 解析后的节点树)
const result = renderer.render(articleData)
if (result.success) {
console.log('渲染成功,高度:', result.height)
} else {
console.error('渲染失败:', result.error)
}
})
}
})重要提示:
articleData必须是app.towxml()方法返回的节点树数据(TowxmlNode类型),不能直接传入 Markdown 文本字符串- 关于
towxml的详细使用方法,请参考 towxml 3.0 使用文档
自定义样式
import { MarkdownRenderer, defaultStyles } from 'towxml-canvas-renderer'
const renderer = new MarkdownRenderer(ctx, width, height, {
...defaultStyles,
padding: 20,
baseFontSize: 32,
colors: {
...defaultStyles.colors,
text: '#000000',
h1: '#1a1a1a',
}
})相关文档
体验小程序
项目提供了一个体验版微信小程序,扫码即可在真实环境下验证 Markdown 渲染效果。

