快速开始
快速开始
本节将带你从零搭建环境,到在网页上绘制第一个 CAD 图形,只需几分钟。
前置条件
- Node.js >= 16(推荐 18+)
- vjmap SDK — vjmapext 是 vjmap 的扩展库,必须配合 vjmap 一起使用
- 一个 vjmap 服务地址和 Access Token(可使用官方测试环境)
获取 Access Token
访问 vjmap 官网 注册账号后即可获得测试服务地址和 Access Token。官方测试环境自带多张 DWG 示例图纸,可直接用于开发调试。
安装
npm 安装(推荐)
npm install vjmap vjmapextCDN / Script 标签
如果不使用打包工具,也可以通过 <script> 标签直接引入:
<!-- vjmap SDK -->
<script src="https://vjmap.com/demo/js/vjmap/vjmap.min.js"></script>
<link rel="stylesheet" href="https://vjmap.com/demo/js/vjmap/vjmap.min.css" />
<!-- vjmapext 扩展库 -->
<script src="https://vjmap.com/demo/js/vjmapext/vjmapext.min.js"></script>版本对应
请确保 vjmap(>=1.0.161)和 vjmapext 版本兼容。建议始终使用同一时期发布的版本组合。
地图容器布局要求
无论用哪种方式集成,vjmap.Map 的容器(下文示例里的 #map / #map-container)都必须在挂载之前就具有可见的宽高,否则首屏会出现“一闪就没了/只剩深色背景”的现象。两种写法都可以:
/* 写法 A:撑满视口(最稳妥,推荐给 demo / 全屏应用) */
#map { width: 100vw; height: 100vh; }
/* 写法 B:绝对定位贴到定位祖先的四个边 */
.page-wrap { position: relative; } /* 祖先必须是 relative / absolute / fixed */
#map { position: absolute; inset: 0; }createUI 不会改动你已有的容器定位
mapcadLayer.createUI() 会把工具栏、属性面板等以 position:absolute 覆盖在地图容器里。从 v0.1 起,只有当容器的 computedStyle.position 为 static 时,SDK 才会临时给它补一个 position:relative,并在 dispose() 时精确还原;已有的 absolute / fixed / relative / sticky 会被完整保留,不会像旧版本那样被覆盖导致容器高度塌成 0。
使用 Vite 搭建项目(推荐)
以下示例以 Vite + Vanilla JS 为例,Vue / React 项目同理。
1. 创建项目
npm create vite@latest my-cad-app -- --template vanilla
cd my-cad-app
npm install vjmap vjmapext2. HTML 模板
在 index.html 中添加地图容器:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>vjmapext 快速开始</title>
<style>
html, body, #map { margin: 0; padding: 0; width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>3. 编写入口文件
在 src/main.js 中编写初始化逻辑:
import vjmap from 'vjmap';
import 'vjmap/dist/vjmap.min.css';
import { MapCadLayer, LineEnt, CircleEnt, TextEnt } from 'vjmapext';
async function main() {
// 1. 创建 vjmap 服务
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'你的AccessToken'
);
// 2. 定义坐标范围与投影
const mapExtent = vjmap.GeoBounds.fromArray([-1000, -1000, 1000, 1000]);
const prj = new vjmap.GeoProjection(mapExtent);
// 3. 创建地图
const map = new vjmap.Map({
container: 'map',
style: svc.rasterBlankStyle(0, 24),
center: prj.toLngLat(mapExtent.center()),
zoom: 1,
pitch: 0,
renderWorldCopies: false
});
map.attach(svc, prj);
await map.onLoad();
// 4. 挂载 MapCadLayer
const mapcadLayer = new MapCadLayer();
map.addControl(mapcadLayer);
mapcadLayer.createUI({ theme: 'dark' });
// 5. 绘制一条红色直线
const line = new LineEnt([-200, 0], [200, 0]);
line.color = 0xff4444;
line.lineWidth = 2;
mapcadLayer.addEntity(line);
// 6. 绘制一个青色圆
const circle = new CircleEnt([0, 200], 100);
circle.color = 0x00ffff;
circle.lineWidth = 2;
mapcadLayer.addEntity(circle);
// 7. 添加文字标注
const text = new TextEnt([-200, -150], 'Hello vjmapext!', 40);
text.color = 0x44ff44;
mapcadLayer.addEntity(text);
}
main();4. 运行
npm run dev打开浏览器即可看到一条红色直线、一个青色圆和一段绿色文字。
核心流程详解
整个初始化过程可以拆分为以下步骤:
关键理解
MapCadLayer 是 vjmap 的 IControl 控件。调用 map.addControl(mapcadLayer) 后,它会自动与地图的坐标系统、渲染循环和交互系统对接。之后通过 addEntity() 添加的所有实体都会自动渲染到地图上。
在 CAD 底图上绘制
除了空白地图,vjmapext 更常见的场景是在已有 DWG 图纸之上进行叠加绘制。
import vjmap from 'vjmap';
import { MapCadLayer, HatchEnt, TextEnt, CircleEnt } from 'vjmapext';
async function main() {
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'你的AccessToken'
);
// 打开一张 CAD 图纸
const res = await svc.openMap({
mapid: '你的图纸ID',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle()
});
if (res.error) { throw new Error(res.error); }
// 从返回的 bounds 创建坐标投影
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
const map = new vjmap.Map({
container: 'map',
style: svc.rasterStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 2,
renderWorldCopies: false
});
map.attach(svc, prj);
map.fitMapBounds();
await map.onLoad();
// 挂载 MapCadLayer
const mapcadLayer = new MapCadLayer();
map.addControl(mapcadLayer);
mapcadLayer.createUI({ theme: 'dark' });
// 在底图上叠加一个高亮区域
const cx = mapExtent.center().x;
const cy = mapExtent.center().y;
const w = mapExtent.width() / 8;
const zone = new HatchEnt([
[cx - w, cy - w],
[cx + w, cy - w],
[cx + w, cy + w],
[cx - w, cy + w]
]);
zone.fillColor = 0x00ffff;
zone.fillOpacity = 0.15;
zone.color = 0x00ffff;
zone.lineWidth = 2;
mapcadLayer.addEntity(zone);
// 添加标签
const label = new TextEnt([cx - w, cy + w * 1.2], '重点区域', w * 0.3);
label.color = 0x00ffff;
mapcadLayer.addEntity(label);
}
main();服务地址
svc.openMap() 需要连接 vjmap 后端服务来解析 DWG 文件。请确保服务地址可访问,且 mapid 对应的图纸已上传到服务端。
创建 UI
MapCadLayer 内置了工具栏、属性面板、命令行等 UI 组件,一行代码即可创建:
mapcadLayer.createUI({ theme: 'dark' });支持的主题:
| 参数 | 说明 |
|---|---|
theme: 'dark' | 深色主题,适合深色底图 |
theme: 'light' | 亮色主题,适合浅色底图 |
创建 UI 后,用户即可通过工具栏使用内置的 30+ 种绘图和编辑命令。
也可以不创建 UI
createUI() 是可选的。如果你只需要通过代码控制绘图(不需要用户交互界面),可以跳过这一步,直接通过 API 创建和管理实体。
编辑与浏览模式
MapCadLayer 有两种运行模式:
- 编辑模式(
edit):可选择、移动、修改实体,执行命令 - 浏览模式(
browse):只读查看,实体不可编辑
// 通过选项设置初始模式
const mapcadLayer = new MapCadLayer({ mode: 'browse' });
// 运行时切换
mapcadLayer.setMode('edit');
mapcadLayer.setMode('browse');默认快捷键 Ctrl + Shift + E 可在两种模式间切换。
MapCadLayer 常用选项
const mapcadLayer = new MapCadLayer({
locale: 'zh', // 语言:'zh' | 'en'
defaultColor: 0x00ff00, // 默认实体颜色(绿色)
mode: 'edit', // 初始模式:'edit' | 'browse'
modeToggleShortcut: 'ctrl+shift+e', // 模式切换快捷键
nudgePixels: 1, // 方向键微移像素数
nudgeRotateDeg: 1, // 微移旋转角度
nudgeScaleFactor: 1.02, // 微移缩放因子
});Vue 3 + Vite 集成
Vue 3 单文件组件里只需关心两件事:保证容器有宽高、在 onMounted 里拿到 map 后再 createUI()。
1. 入口与全局样式
index.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>vjmapext · Vue 3</title>
<style>
html, body, #app { margin: 0; padding: 0; width: 100%; height: 100%; }
body { background: #022b4f; overflow: hidden; }
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>2. 地图组件
src/Map.vue:
<script setup lang="ts">
import 'vjmap/dist/vjmap.min.css'
import vjmap, { type Map } from 'vjmap'
import vjmapext from 'vjmapext'
import { onMounted, onUnmounted } from 'vue'
const { MapCadLayer, CircleEnt, TextEnt } = vjmapext
let map: Map | null = null
onMounted(async () => {
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'你的AccessToken',
)
const res = await svc.openMap({
mapid: '你的图纸ID',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle(),
})
if (res.error) throw new Error(String(res.error))
const mapExtent = vjmap.GeoBounds.fromString(res.bounds)
const prj = new vjmap.GeoProjection(mapExtent)
map = new vjmap.Map({
container: 'map-container',
style: svc.rasterStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 2,
renderWorldCopies: false,
})
map.attach(svc, prj)
map.fitMapBounds()
await map.onLoad()
const cad = new MapCadLayer()
map.addControl(cad)
cad.createUI({ theme: 'dark' })
const c = new CircleEnt([mapExtent.center().x, mapExtent.center().y], mapExtent.width() / 8)
c.color = 0x00ffff
cad.addEntity(c)
const t = new TextEnt([mapExtent.center().x, mapExtent.center().y], 'Hello vjmapext', mapExtent.width() / 40)
t.color = 0x00ffff
cad.addEntity(t)
})
onUnmounted(() => {
map?.remove()
map = null
})
</script>
<template>
<div id="map-container" />
</template>
<style scoped>
#map-container {
position: absolute;
inset: 0;
background: #022b4f;
}
</style>依赖安装
推荐以 npm 包方式引入:
npm install vjmap vjmapext纯 HTML <script> 引入
不使用打包工具时,vjmap 与 vjmapext 都可以直接通过 <script> 引入,所有导出都挂在全局 vjmap / vjmapext 对象上。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>vjmapext · plain HTML</title>
<link rel="stylesheet" href="./vjmap/vjmap.min.css">
<script src="./vjmap/vjmap.min.js"></script>
<script src="./vjmapext/vjmapext.min.js"></script>
</head>
<body style="margin:0;overflow:hidden;background:#022B4F;">
<div id="map" style="position:absolute;inset:0;background:#022B4F;"></div>
<script>
(async () => {
const svc = new vjmap.Service(
'https://vjmap.com/server/api/v1',
'你的AccessToken',
);
const res = await svc.openMap({
mapid: '你的图纸ID',
mapopenway: vjmap.MapOpenWay.GeomRender,
style: vjmap.openMapDarkStyle(),
});
if (res.error) { console.error(res.error); return; }
const mapExtent = vjmap.GeoBounds.fromString(res.bounds);
const prj = new vjmap.GeoProjection(mapExtent);
const map = new vjmap.Map({
container: 'map',
style: svc.rasterStyle(),
center: prj.toLngLat(mapExtent.center()),
zoom: 2,
renderWorldCopies: false,
});
map.attach(svc, prj);
map.fitMapBounds();
await map.onLoad();
const { MapCadLayer, CircleEnt, TextEnt } = vjmapext;
const cad = new MapCadLayer();
map.addControl(cad);
cad.createUI({ theme: 'dark' });
const c = new CircleEnt([mapExtent.center().x, mapExtent.center().y], mapExtent.width() / 8);
c.color = 0x00ffff;
cad.addEntity(c);
})();
</script>
</body>
</html>资源路径
示例里相对路径 ./vjmap/*、./vjmapext/* 只是占位。实际用时请替换为你自己的 CDN 地址或静态资源目录;vjmap.min.js 必须早于 vjmapext.min.js 加载,否则 vjmapext 中对 vjmap 的引用会失败。
在线示例
更多完整示例请访问 vjmapext Demo Playground,涵盖所有实体类型、命令操作、动画特效等。