1. 项目概述
最近在整理技术文档时,发现一个痛点:如何快速绘制清晰美观的软件架构图?市面上的工具要么太重(如Visio),要么需要学习复杂语法(如PlantUML),要么依赖特定平台。于是,我开发了一个基于纯原生Web技术的Markdown架构图生成器,完全开源且零依赖。
这个工具的核心特点是:
- 使用简单的Markdown语法定义架构层次
- 支持横向(@row)和纵向(@col)两种布局模式
- 具备无限画布交互功能(拖拽平移+鼠标中心缩放)
- 实时预览+样式自定义+响应式设计
- 纯前端实现,一个HTML文件搞定所有功能
2. 核心功能解析
2.1 Markdown语法解析
工具采用极简的Markdown语法定义架构:
# 层级标题 [布局方向] - 模块名称: 模块描述 - 模块名称: 模块描述布局指令说明:
@row:模块横向排列(适合展示同级服务)@col:模块纵向排列(默认,适合展示调用链)
实现原理:
- 通过正则表达式
/^#(.+?)(@row|@col)?/匹配标题行 - 使用字符串分割处理模块描述
- 构建层级化的JSON数据结构
2.2 智能布局引擎
横向布局算法
// 计算模块宽度 const availableWidth = layerWidth - 2*padding - (count-1)*gap const modWidth = count > 0 ? availableWidth / count : 0 // 定位模块 layer.modules.forEach((mod, mIdx) => { const modX = padding + mIdx*(modWidth + gap) const modY = currentY + 55 drawModule(modX, modY, modWidth, moduleHeight, mod) })纵向布局算法
layer.modules.forEach((mod, mIdx) => { const modX = padding const modY = currentY + 50 + (mIdx*(moduleHeight + 10)) const modWidth = layerWidth - 2*padding drawModule(modX, modY, modWidth, moduleHeight, mod) })2.3 无限画布实现
关键技术点:
- 使用Canvas 2D的
transform矩阵 - 维护
scale和offset状态 - 滚轮缩放时重新计算视口中心
核心代码:
// 滚轮缩放处理 container.addEventListener('wheel', (e) => { const rect = canvas.getBoundingClientRect() const mouseX = e.clientX - rect.left const mouseY = e.clientY - rect.top const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1 // 关键公式:保持鼠标位置内容不变 offsetX = mouseX - (mouseX - offsetX)*zoomFactor offsetY = mouseY - (mouseY - offsetY)*zoomFactor scale *= zoomFactor draw() })3. 使用指南
3.1 快速开始
- 克隆仓库或下载HTML文件
- 浏览器直接打开
index.html - 左侧编辑Markdown,右侧实时预览
3.2 样式自定义
支持调整:
- 画布背景色
- 模块填充色
- 模块边框色
- 模块文字色
提示:使用相近色系(如蓝色系)能让架构图更专业
3.3 导出图片
目前支持两种方式:
- 浏览器开发者工具截图(F12 → 截图功能)
- 系统截图工具(如Snipaste)
4. 技术实现细节
4.1 核心架构
graph TD A[Markdown输入] --> B[语法解析器] B --> C[布局引擎] C --> D[Canvas渲染] D --> E[交互处理]4.2 关键算法
网格背景绘制
function drawGrid() { const gridSize = 40 * scale const startX = (offsetX % gridSize) - gridSize const startY = (offsetY % gridSize) - gridSize ctx.beginPath() for(let x=startX; x<canvas.width; x+=gridSize) { ctx.moveTo(x,0); ctx.lineTo(x,canvas.height) } // 纵线绘制同理... ctx.stroke() }模块渲染优化
- 使用
ctx.roundRect绘制圆角矩形 - 通过
clip()防止文字溢出 - 采用
shadowColor实现柔和阴影
5. 扩展开发指南
5.1 添加新功能
推荐扩展方向:
- PNG导出功能(使用canvas.toDataURL)
- 节点拖拽编辑(需实现碰撞检测)
- 自定义连接线(需要路径计算算法)
5.2 修改布局算法
关键函数:
parseText():文本解析入口draw():主绘制流程drawModule():单个模块渲染
5.3 样式定制
修改<style>标签中的CSS变量:
:root { --module-bg: #e3f2fd; --module-border: #2196f3; --text-color: #0d47a1; }6. 性能优化建议
节流渲染:对resize和input事件添加100ms延迟
let renderTimer input.addEventListener('input', () => { clearTimeout(renderTimer) renderTimer = setTimeout(draw, 100) })离屏Canvas:复杂架构可预渲染到隐藏Canvas
分层渲染:将网格背景与模块分层绘制
7. 常见问题排查
7.1 文字显示不全
解决方案:
- 检查
ctx.clip()是否正确应用 - 调整模块内边距(padding)
- 减小字体大小或增加模块高度
7.2 缩放卡顿
优化方向:
- 降低网格密度
- 简化阴影效果
- 使用
requestAnimationFrame
7.3 浏览器兼容性
已知问题:
- Safari对roundRect支持较晚(需polyfill)
- 旧版Edge可能需启用实验性功能
8. 设计思考与演进
最初版本仅支持静态渲染,经过三次迭代才加入:
- 动态布局方向(v0.2)
- 无限画布交互(v0.5)
- 实时样式配置(v1.0)
关键决策点:
- 放弃使用第三方库(如D3.js)以保持轻量
- 采用Markdown而非JSON作为输入格式
- 优先实现核心功能而非完美UI
9. 应用场景案例
9.1 微服务架构文档
# 网关层 @row - API Gateway: 路由鉴权 - Load Balancer: 流量分发 # 业务服务 @col - 用户服务: 用户管理 - 订单服务: 交易处理 - 支付服务: 第三方对接9.2 前端项目结构
# 应用架构 @col - 组件层: React/Vue组件 - 状态管理: Redux/Pinia - API层: Axios封装9.3 系统部署拓扑
# 生产环境 @row - Web服务器: Nginx集群 - 应用服务器: Docker Swarm - 数据库: MySQL主从10. 同类方案对比
| 特性 | 本工具 | PlantUML | Draw.io |
|---|---|---|---|
| 学习成本 | 低 | 中 | 中 |
| 交互性 | 高 | 无 | 高 |
| 部署复杂度 | 零 | 需Java环境 | 需注册 |
| 定制灵活性 | 代码级 | 语法限制 | UI限制 |
| 实时协作 | 否 | 否 | 是 |
11. 开发心得
- Canvas坐标系:理解全局坐标与局部坐标的转换是关键
- 矩阵变换:掌握
translate+scale的组合运用 - 事件处理:注意移除监听器防止内存泄漏
- 响应式设计:监听resize事件时要防抖
踩坑记录:最初未考虑缩放时的文字清晰度,后来发现需要根据scale动态调整字体大小
12. 未来规划
- 增加导出为SVG功能
- 支持模块折叠/展开
- 添加箭头连线功能
- 实现本地存储自动保存
这个项目完全开源,欢迎在GitHub上提交PR或issue。对于想要学习Canvas和前端架构的开发者,代码中包含详细注释,是非常好的学习素材。