唯杰地图 logo唯杰地图
VJMAP
EN

draw-tools

vjmap/references/interaction/draw-tools.md 7.5 KB203 行
原文

本文回答:vjmap.Draw.Tool 控件用法 —— 选项 / 模式 / API / 事件 / 样式。

一、创建 Draw 控件

const draw = new vjmap.Draw.Tool({
  keybindings: true,    // 键盘交互
  touchEnabled: true,
  boxSelect: true,      // shift+拖 框选
  snap: true,           // 捕捉
  guides: true,         // 对齐辅助线(经过已有顶点的水平/竖直参考线,不是网格)
  snapOptions: {
    snapPx: 15,
    snapToMidPoints: false,
    snapVertexPriorityDistance: 1.25,
    snapGridPx: 2000     // 辅助线的半长(像素),不是网格间距
  },
  controls: {
    point: true, line_string: true, polygon: true, trash: true
  },
  displayControlsDefault: true
});
map.addControl(draw, "top-right");

二、模式(mode)

mode 说明
static 非编辑态,可 hover / click 高亮
simple_select 选择、删除、拖整体
direct_select 选、删、拖顶点
draw_point 画点
draw_line_string 画线
draw_polygon 画多边形
draw_circle 画圆
draw_rectangle 画矩形
splitLineMode 切割线
cutPolygonMode 切割多边形

切换:

draw.changeMode("draw_line_string");
draw.getMode();   // 当前模式

坑位:changeMode("direct_select") 必须带 { featureId },否则抛 You must provide a featureId;其余模式可裸名切换。

三、常用 API

// 增/查
const ids = draw.add({ type: "Feature", geometry: { type: "Point", coordinates: [0,0] } });
draw.get(id);
draw.getAll();
draw.getSelected();
draw.getSelectedIds();

// 删
draw.delete(id);
draw.delete([id1, id2]);
draw.deleteAll();
draw.trash();      // 调当前模式的"删除"动作

// 批量替换
draw.set(featureCollection);

// 历史
draw.undo();
draw.redo();
draw.clearHistory();

// 刷新(改 geometry 后调)
draw.forceRefresh();

// 组合(⚠️ 只在 simple_select 模式下可用(组合需选中 ≥2 个)——其它模式下调用抛
// "onCombineFeatures is not a function",组合 / 取消组合处理器只注册在 simple_select 上)
draw.combineFeatures();
draw.uncombineFeatures();

四、设置 Feature 属性

draw.setFeatureProperty(id, "color", "#f00");
draw.setFeatureProperty(id, "line_width", 3);
draw.setFeatureProperty(id, "hoverPointer", true);  // 浏览模式高亮
draw.setFeatureProperty(id, "hover_color", "#ff0");
draw.setFeatureProperty(id, "isLocked", true);       // 锁定
draw.setFeatureProperty(id, "isOff", true);          // 隐藏
draw.setFeatureProperty(id, "noSnap", true);         // 禁捕捉
draw.setFeatureProperty(id, "disable_edit", true);   // 禁编辑
draw.setFeatureProperty(id, "minZoom", 5);
draw.setFeatureProperty(id, "maxZoom", 10);
draw.setFeatureProperty(id, "extrusionHeight", 100); // 多边形拉伸

点的额外属性:

draw.setFeatureProperty(id, "icon_image", "my-icon");
draw.setFeatureProperty(id, "icon_size", 1.2);
draw.setFeatureProperty(id, "text", "A");
draw.setFeatureProperty(id, "text_size", 22);
draw.setFeatureProperty(id, "symbol", true);         // 带图标+文字

五、事件

map.on("draw.create", e => console.log("新建", e.features));
map.on("draw.update", e => console.log("更新", e.action, e.features));
map.on("draw.delete", e => console.log("删除", e.features));
map.on("draw.selectionchange", e => console.log("选中", e.features));
map.on("draw.modechange", e => console.log("模式", e.mode));
map.on("draw.render", () => console.log("重绘"));
map.on("draw.actionable", e => console.log("可操作", e.actions));
map.on("draw.updatecoordinate", e => console.log("坐标", e.lnglat));
map.on("draw.simpleselect.contextmenu", e => { /* 右键 */ });
map.on("draw.directselect.contextmenu", e => { /* 右键 */ });
map.on("draw.custom", e => {
  if (e.customName === "clickedLocked") { /* 点了锁定图形 */ }
});

// static 模式
map.on("draw.static.mouseenter", e => { /* popup */ });
map.on("draw.static.mouseleave", e => { /* popup remove */ });
map.on("draw.static.click", e => { /* 点击 */ });

六、自定义控件按钮

⚠️ addControls 里与内置按钮同 id 的项会被丢弃(内置 id:drawCircle / drawRectangle / splitLine / cutPolygon / undo / redo / snap_mode_snap / snap_mode_grid),内置按钮照常保留——自定义按钮要用新 id;要隐藏内置按钮设 opts.controls.<id> = false(如 opts.controls.splitLine = false)。

const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
opts.addControls = [
  {
    id: "myCircle", title: "画圆", className: "vjmap-map-draw_circle",
    onActivate: (ctx) => ctx.api.changeMode("draw_circle")
  },
  {
    id: "mySplitLine", title: "切线", className: "vjmap-map-draw-cut-line",
    onActivate: (ctx) => ctx.api.changeMode("splitLineMode", { spliter: "line_string" })
  },
  {
    id: "myUndo", title: "撤销", className: "vjmap-map-draw_undo",
    onActivate: (ctx) => ctx.api.undo()
  },
  {
    id: "mySnap", title: "捕捉", isCheckbox: true, checked: true,
    className: "snap_mode_snap",
    onActivate: (ctx, e) => ctx.options.snap = e.target.checked
  }
];
// 隐藏同功能的内置按钮
Object.assign(opts.controls, { drawCircle: false, splitLine: false, undo: false, snap_mode_snap: false });
const draw = new vjmap.Draw.Tool(opts);

七、绘制时禁用键盘

map.setCustomKeyValue("_disable_drawkey_backspacekey", true);
map.setCustomKeyValue("_disable_drawkey_escapekey", true);
map.setCustomKeyValue("_disable_drawkey_enterkey", true);

八、默认样式修改

Draw.defaultOptions() 只是浅拷贝,styles / controls / addControls 是全局共享对象——直接改会污染之后所有 Draw 实例(含 actionDraw* 内部建的临时实例),先 vjmap.cloneDeep:

const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
opts.styles[7].paint["line-width"] = 4;   // 线宽
opts.styles[7].paint["line-color"] = "#0f0";
opts.midpoints = false;                    // 不显示中点
const draw = new vjmap.Draw.Tool(opts);

九、仅 action 模式(不显示工具栏)

const opts = vjmap.cloneDeep(vjmap.Draw.defaultOptions());
opts.isActionDrawMode = true;  // 不生成按钮
const draw = new vjmap.Draw.Tool(opts);
map.addControl(draw, "top-right");
// 用自己的实例驱动绘制:changeMode + 按 styleId 过滤 draw.create
map.on("draw.create", e => { if (e.styleId === draw.options.styleId) console.log(e.features); });
draw.changeMode("draw_line_string");

注意:vjmap.Draw.actionDraw* 每次都自建并在结束后移除一个临时 Tool,不会画进你的 draw 实例;要把结果放进自己的实例,拿 resolve 的 features 再 draw.add(...)。

十、常见坑

  1. setFeatureProperty 后不可见 → draw.forceRefresh()。
  2. 触摸端体验差 → 调大 touchBuffer(默认 25)。
  3. 多个 Draw 实例事件乱:不同 draw 的 options.styleId 不同,在事件回调里比较 e.styleId 过滤。
  4. draw.add(feature) 但 id 已存在 → 实际是 update,不是新建;要唯一可以 delete + add。
  5. 程序化增删不进历史、不发事件:add/delete/set/deleteAll 只写 store + render(api.add 不发 draw.create,api.delete 走 silent:true)。历史记录只在 draw.create/update/delete/combine 事件的监听里生成——即 undo/redo 只覆盖交互操作,对程序化 add 是 no-op。需要可撤销的程序化改动要自己管快照,或走 draw.set + 自建历史。