













在之前的文档中,我们已对 MxCAD 产品进行了宏观介绍,并完成了开发环境的初步搭建与 Demo 运行。然而,从“运行官方示例”到“构建自有业务系统”之间,仍存在概念理解与工程落地的鸿沟。本篇文档的核心目标在于填补这一空白,重点解决以下两个关键问题:
概念理清:深入解析 MxCAD 与 MxDraw 的技术定位与职能边界。明确 MxCAD 作为“编辑内核”负责原图数据的深度操作,而 MxDraw 作为“渲染引擎”专注于图纸浏览与非侵入式批注。
工程落地:指导开发者在自己的项目中,从零构建一个具备图纸加载、视图浏览、独立批注功能的最小可行性产品(MVP),下面以vue项目为讲解示例。
通过本文的学习,您将掌握如何根据业务需求(是仅需审阅批注,还是需要修改原图)选择合适的技术组件,并能够独立搭建起一个可运行的 Web CAD 基础框架,为后续开发复杂的在线设计平台奠定坚实基础。

在梦想云图(DreamCloud CAD)的技术体系中,MxCAD 与 MxDraw 是两个核心组件。尽管二者在底层紧密耦合,但在业务逻辑与应用场景上存在明确的职能划分。理解二者的区别是进行二次开发的前提。
定义:MxCAD 是基于 C++ 核心通过 WebAssembly (Wasm) 移植至 Web 端的专业 CAD 开发框架。
核心职能:图纸数据编辑与深度业务逻辑。
适用场景:Web 端在线绘图、图纸深化设计、自动化生成报表、基于图纸数据的业务系统集成。
在实际的技术架构中,MxCAD 与 MxDraw 并非孤立存在,而是呈现出紧密的层级协作与演进关系:

MxDraw 是梦想云图技术体系的基础层。它提供了核心的 HTML5 Canvas 渲染能力、基础的图形交互逻辑以及轻量级的批注功能。其设计初衷是解决“在网页上快速、高效地展示和标注 CAD 图纸”这一核心痛点。
MxCAD 则是基于 MxDraw 的全功能升级版本。它在保留并复用 MxDraw 所有渲染与交互优势的基础上,通过引入 C++ 内核(编译为 WebAssembly)和 TypeScript 业务框架,扩展了完整的 CAD 数据解析、实体编辑、几何计算及复杂命令处理能力。
简而言之:MxCAD = MxDraw (渲染与交互) + CAD 专业内核 (数据与逻辑)。
默认集成:在大多数应用场景下,开发者引入 MxCAD 开发包时,实际上已经包含了 MxDraw 的功能模块。MxCAD 内部会自动调用 MxDraw 进行图形绘制和用户交互监听。
按需调用:
无缝切换:由于二者同源,开发者可以在同一个项目中根据权限或功能模块,动态切换“只读/批注模式”与“编辑模式”,无需更换底层引擎,保证了用户体验的一致性与技术架构的统一性。
这种架构设计既保证了轻量级应用的极致性能,又为专业级应用提供了无限扩展的可能。
环境要求
Node.js: ≥ v16
包管理器: npm / pnpm
构建工具:
Vite (推荐,配置简单,开发速度快)
Webpack (支持,需配置 worker-loader 或相关 WASM 处理规则)
其他:Rollup, Parcel 等支持自定义 Loader/Plugin 的构建工具均可。
后端服务: 需运行云图开发包提供的图纸转换服务,用于处理 DWG/DXF 转 MXWEB。
可直接开启云图开发包 3000 端口下的服务。
也可自行根据图纸转换文档规则设置新的转换接口。
创建或集成项目
您可新建项目或在现有项目中添加 CAD 模块:
# 新建 Vue3 + TypeScript 项目 npm create vite@latest cad-viewer -- --template vue-ts cd cad-viewer npm install
安装核心依赖
# 安装 mxcad 核心包
npm install mxcad
# 若使用 pnpm,请额外安装 mxdraw (通常 mxcad 已包含,视具体版本策略而定)
# pnpm add mxdraw
配置构建工具支持多线程
MxCAD 的高性能渲染依赖 SharedArrayBuffer,这要求服务器响应头必须包含 Cross-Origin-Opener-Policy: same-origin 和 Cross-Origin-Embedder-Policy: require-corp。无论使用何种打包工具(Webpack、Rollup 等),都必须进行此配置,否则引擎将无法启动或自动降级。
// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { // 开发环境:直接在此处配置响应头 headers: { "Cross-Origin-Opener-Policy": "same-origin", "Cross-Origin-Embedder-Policy": "require-corp", } }, build: { target: 'esnext' // 确保编译目标支持最新特性 } })
模板结构:Canvas 与文件上传控件
在组件模板中定义渲染容器和文件选择器,并添加控制按钮。
<template>
<!-- CAD 渲染区域 -->
<div style="width: 100vw; height: 95vh; overflow: hidden;">
<canvas id="myCanvas"></canvas>
</div>
<!-- 控制栏 -->
<div style="margin-top: 10px; padding: 10px; background: #f5f5f5;">
<!-- 隐藏的文件输入框 -->
<input
type="file"
ref="fileInput"
accept=".dwg,.dxf,.mxweb"
@change="openDwgFile"
style="display:none"
/>
<!-- 功能按钮 -->
<button @click="$refs.fileInput.click()" :disabled="!mxcadReady">打开图纸</button>
<button @click="startCloudMark" :disabled="!mxcadReady">绘制云线批注</button>
<button @click="saveMarkup" :disabled="!mxcadReady">保存批注</button>
<button @click="loadMarkup" :disabled="!mxcadReady">加载批注</button>
</div>
</template>
初始化 MxCAD 实例
在 Vue 组件的 onMounted 生命周期中调用 createMxCad,完成 Canvas 绑定、WASM 核心文件路径定位、字体库路径设置以及初始化完成的回调处理。
<script setup lang="ts"> import { onMounted, ref } from 'vue'; import { createMxCad, MxCpp } from 'mxcad'; const mxcadReady = ref(false); const fileInput = ref<HTMLInputElement | null>(null); onMounted(() => { // 自动检测浏览器环境:支持 SharedArrayBuffer 则启用多线程 (2d),否则降级为单线程 (2d-st) const mode = "SharedArrayBuffer" in window ? "2d" : "2d-st"; createMxCad({ canvas: "#myCanvas", // 动态定位 WASM 文件路径,兼容 Vite/Webpack 等构建工具 locateFile: (fileName: string) => { return new URL( `../../node_modules/mxcad/dist/wasm/${mode}/${fileName}`, import.meta.url ).href; }, fileUrl: "", // 初始不加载文件,等待用户通过 UI 选择 fontspath: new URL( "../../node_modules/mxcad/dist/fonts", import.meta.url ).href, // 初始化完成回调 onInit: () => { console.log("MxCAD 引擎初始化完成"); mxcadReady.value = true; } }); }); </script>
更多配置项: createMxCad 支持丰富的自定义配置(如背景色、多选配置、视区移动方式等)。如需了解完整的配置参数列表及详细说明,请访问官方文档:[MxCadConfig 配置接口文档。
重要说明:MxCAD 文件格式限制
MxCAD 引擎内核仅能直接解析和打开特定的 .mxweb 格式文件。它无法直接在浏览器端读取原生的 AutoCAD 格式(如 .dwg 或 .dxf)。
因此,在实现“打开图纸”功能时,必须遵循以下逻辑:
判断格式:检查用户选择的文件后缀。
格式转换:如果文件是 .dwg 或 .dxf,必须先将其上传至后端转换服务,由服务端将其转换为 .mxweb 格式。
加载渲染:只有获取到 .mxweb 格式的文件流或 URL 后,才能调用 mxcad.openWebFile() 进行加载和渲染。
若跳过转换步骤直接尝试加载 DWG 文件,MxCAD 将报错或无法显示任何内容。

文件选择与处理流程
基于上述限制,我们的代码逻辑分为两条路径:
跨域处理配置
由于前端开发服务器(默认 localhost:5173)与图纸转换服务(localhost:3000)端口不同,直接请求会触发浏览器的跨域限制 (CORS)。
无论使用何种打包工具(Webpack、Rollup 等),在开发环境下都必须配置代理(Proxy)或将请求转发至同源,以解决跨域问题。生产环境通常通过 Nginx 反向代理统一入口来解决。
以下以 Vite 为例演示开发环境的代理配置:
修改 vite.config.ts,添加 server.proxy 配置,将 /api 或特定路径的请求转发至转换服务:
// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { // ....其他配置 // 开发环境跨域代理配置 proxy: { // 匹配所有以 /api 开头的请求(建议前端请求时加上 /api 前缀) '/api': { target: 'http://localhost:3000', // 图纸转换服务地址 changeOrigin: true, // 修改 Host 头为 target rewrite: (path) => path.replace(/^\/api/, ''), // 去掉 /api 前缀再转发 }, // 或者直接代理特定的转换接口路径 //'/uploadfile': { // target: 'http://localhost:3000', // changeOrigin: true, // }, // '/demo': { // target: 'http://localhost:3000', // changeOrigin: true, // } } } })
前端代码适配示例: 配置代理后,前端请求地址需调整为相对路径或带前缀的路径:
// 原地址:http://localhost:3000/uploadfile // 代理后地址:/api/uploadfile (需在 proxy 中配置 rewrite) 或直接 /uploadfile const res = await fetch("/api/uploadfile", { // Vite 会自动拦截并转发到 localhost:3000/uploadfile method: "POST", body: formData, });
代码实现:打开网络图纸
图纸的加载与渲染核心依赖于 MxCAD 提供的 openWebFile API。该接口支持从网络 URL 直接加载 .mxweb 或转换后的图纸数据,并提供了丰富的参数控制(如是否使用多线程、加载策略等)。
<script setup lang="ts"> // ... 上述初始化代码 ... // 打开图纸处理函数 const openDwgFile = async (event: Event) => { const target = event.target as HTMLInputElement; const file = target.files?.[0]; if (!file) return; const fileName = file.name; // 获取文件后缀并转为小写 const fileExt = fileName.slice(fileName.lastIndexOf(".")).toLowerCase(); const mxcad = MxCpp.getCurrentMxCAD(); if (fileExt === ".dwg" || fileExt === ".dxf") { // DWG/DXF 需要后端转换 const formData = new FormData(); formData.append("file", file); try { // 调用代理后的上传接口 const res = await fetch("/upfile/mxcad", { method: "POST", body: formData, }); if (!res.ok) throw new Error(`请求失败:${res.status}`); // 约定:转换后的文件位于 /demo/ 目录下,文件名不变,后缀变为 .mxweb const mxwebFullUrl = `/demo/${fileName}.mxweb`; mxcad.openWebFile(mxwebFullUrl); } catch (err) { console.error(err); alert("上传或转换失败,请检查后端服务是否启动"); } } else if (fileExt === ".mxweb") { // MXWEB 直接读取 const tempUrl = URL.createObjectURL(file); mxcad.openWebFile(tempUrl); } else { alert("不支持该文件格式,请上传 .dwg, .dxf 或 .mxweb"); } // 重置 input value,允许重复选择同一文件 target.value = ""; }; </script>
关键点说明:
创建批注(以审图云线为例)
此功能通过 MxDraw 提供的交互接口,让用户在图上绘制云线并添加文字说明。
import { MxFun, MrxDbgUiPrPoint, McEdGetPointWorldDrawObject, MrxDbgUiPrBaseReturn, MxDbRectBoxLeadComment, } from "mxdraw"; // 绘制云线批注函数 function startCloudMark() { const point = new MrxDbgUiPrPoint(); // 获取点交互对象 const mxDraw = MxFun.getCurrentDraw(); // 获取当前绘图对象 const worldDrawComment = new McEdGetPointWorldDrawObject(); // 动态绘制辅助对象 const mxCheckDraw = new MxDbRectBoxLeadComment(); // 云线批注实体对象 // 设置云线半径(屏幕坐标转文档坐标) mxCheckDraw.radius = MxFun.screenCoordLong2Doc(5); mxCheckDraw.setLineWidth(3); mxCheckDraw.setLineWidthByPixels(true); // 第一步:获取起始点 point.setMessage("\n云线框起始点: "); point.go((status) => { if (status != MrxDbgUiPrBaseReturn.kOk) return; mxCheckDraw.point1 = point.value(); // 动态预览:移动鼠标时绘制矩形 worldDrawComment.setDraw((currentPoint) => { mxCheckDraw.point2 = currentPoint; worldDrawComment.drawCustomEntity(mxCheckDraw); }); point.setUserDraw(worldDrawComment); point.setMessage("\n云线框结束点: "); // 第二步:获取结束点 point.go((status) => { if (status != MrxDbgUiPrBaseReturn.kOk) return; mxCheckDraw.point2 = point.value(); // 动态预览:确定文本位置 worldDrawComment.setDraw((currentPoint) => { mxCheckDraw.point3 = currentPoint; worldDrawComment.drawCustomEntity(mxCheckDraw); }); // 设置批注文本属性 mxCheckDraw.text = "审图批注"; mxCheckDraw.textWidth = MxFun.screenCoordLong2Doc(100); mxCheckDraw.textHeight = MxFun.screenCoordLong2Doc(50); mxCheckDraw.fixedSize = true; if (mxCheckDraw.fixedSize) { mxCheckDraw.textHeight = 20; mxCheckDraw.textWidth = 130; } point.setMessage("\n审图标注点: "); // 第三步:获取文本引出点 point.go((status) => { if (status != MrxDbgUiPrBaseReturn.kOk) return; mxCheckDraw.point3 = point.value(); // 将实体添加到图纸数据库 mxDraw.addMxEntity(mxCheckDraw); }); }); }); }
绘制效果如下所示:

保存批注数据
使用官方推荐的 saveMxEntityToJson() 方法,将当前图纸上的所有非原生实体(即批注)序列化为 JSON 字符串。本示例暂存至 localStorage 模拟数据库存储。
import { MxFun } from "mxdraw";
// 保存图纸批注
const saveMarkup = () => {
const draw = MxFun.getCurrentDraw();
if (!draw) return;
// 序列化批注数据
const jsonData = draw.saveMxEntityToJson();
localStorage.setItem("mx-markup-data", jsonData);
alert("批注已保存至本地存储");
};
回显批注数据
当用户再次打开图纸时,从存储中读取 JSON 数据,并调用 loadMxEntityFromJson() 还原批注。
import { MxFun } from "mxdraw";
// 恢复图纸批注
const loadMarkup = () => {
const draw = MxFun.getCurrentDraw();
if (!draw) return;
const jsonData = localStorage.getItem("mx-markup-data");
if (jsonData) {
draw.loadMxEntityFromJson(jsonData);
alert("批注已加载");
} else {
alert("未找到保存的批注数据");
}
};
最佳实践提示:在实际业务中,应将 saveMarkup 生成的 JSON 数据通过 AJAX/Fetch 发送给后端,与图纸 ID 关联存储;loadMarkup 应在图纸加载完成后(监听 onOpenFileComplete 回调)自动从后端拉取数据并执行加载。
启动服务:
测试流程:
路径配置敏感性
Wasm 文件和字体文件的路径配置必须精确。若控制台出现 404 错误或 Wasm 编译失败,请首先检查 public 目录结构及初始化参数中的 locateFile 和 fontspath 路径字符串是否正确。
跨域隔离策略 (COOP/COEP)
较新版本的 MxCAD 利用 SharedArrayBuffer 进行多线程优化以提升性能。若在 Chrome/Edge 中遇到 SharedArrayBuffer is not defined 相关报错,必须在 Web 服务器(或 Vite 配置)的响应头中严格配置:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
字体缺失处理
若图纸文字显示为问号或乱码,通常是因为缺少对应的 SHX 字体文件。请确保开发包中的 fonts 目录已完整部署到项目中,或在后端转换服务中配置字体映射。
大文件性能
对于超过 50MB 的超大图纸,建议在加载前进行前端提示。可考虑启用 SDK 中的按需加载功能或简化显示模式(如关闭抗锯齿、隐藏填充图案)以优化渲染帧率。
批注数据管理
批注数据是独立于原图存在的。务必在前端或后端建立“图纸 ID - 批注 JSON”的映射关系,避免不同图纸间的批注混淆。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。