我当前使用的版本是:
"@mapbox/mapbox-gl-draw": "^1.4.1",
"@mapbox/mapbox-gl-draw-static-mode": "^1.0.1",
"mapbox-gl-draw-circle": "^1.1.2",
"mapbox-gl-draw-rectangle-mode": "^1.0.4",
api地址:
@mapbox/mapbox-gl-draw:https://github.com/mapbox/mapbox-gl-draw/blob/main/docs/API.md
@mapbox/mapbox-gl-draw是主要的库,本身包含的模式有普通选择、点、线、多边形等。另外三个分别是静态模式、圆模式、矩形模式三种扩展,对应的介绍和api在Modes章节点链接查看。
ps. 如果你使用的是vue3+vite,mapbox-gl-draw-circle使用commonjs语法在打包时可能会坑你一把,而我至今还没找到解决办法。但相关的方法已经写了,所以也会放在下面,开发环境使用没有问题。
注册绘制工具
需要注意的是,MapboxDraw可以在map定义后就添加到map,但必须要等到地图load()以后才能使用。
import MapboxDraw from "@mapbox/mapbox-gl-draw";
import { CircleMode, DragCircleMode, DirectMode, SimpleSelectMode } from "mapbox-gl-draw-circle"; // 打包可能会遇到问题
import DrawRectangle from "mapbox-gl-draw-rectangle-mode";
import StaticMode from "@mapbox/mapbox-gl-draw-static-mode";
import "@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css";
let draw = new MapboxDraw({
userProperties: true,
displayControlsDefault: false, // 不显示默认绘制工具条
modes: {
...MapboxDraw.modes,
draw_circle: CircleMode, // 打包可能会遇到问题
drag_circle: DragCircleMode, // 打包可能会遇到问题
direct_select: DirectMode, // 打包可能会遇到问题
simple_select: SimpleSelectMode, // 打包可能会遇到问题
draw_rectangle: DrawRectangle,
static: StaticMode,
window.draw = draw; // 挂载到全局
map.addControl(draw); // 添加到map对象
自定义绘制类CustomDraw.js
export default class CustomDraw {
constructor(drawType) {
this._map = window.map;
this._draw = window.draw;
this.drawType = drawType || "single"; // 默认单图形模式,即新画出新图形时清除之前画的图形
if (this.drawType === "view") { // view 仅查看模式,禁止编辑
this.enableEdit(false);
} else {
this.enableEdit(true);
initListener(cb) {
let _this = this;
// 清除之前的监听
if (this._map._listeners["draw.create"]) {
this._map._listeners["draw.create"] = undefined;
if (this._map._listeners["draw.update"]) {
this._map._listeners["draw.update"] = undefined;
if (this._map._listeners["draw.delete"]) {
this._map._listeners["draw.delete"] = undefined;
// 重新初始化监听
this._map.on("draw.create", function (e) {
console.log("draw.create");
if (typeof cb === "function") cb(e.features);
// 清除之前画出的图形
let thisId = e.features[0].id;
_this.singleClear(thisId);
this._map.on("draw.update", (e) => {
console.log("draw.update");
if (typeof cb === "function") cb(e.features);
this._map.on("draw.delete", (e) => {
console.log("draw.delete");
if (typeof cb === "function") cb(e.features);
// 进入矩形模式
addRectangle(cb) {
this._draw.changeMode("draw_rectangle", {
// The id of the LineString to continue drawing
featureId: "",
// The point to continue drawing from
from: [],
this.initListener(cb); // 创建监听,画完后获取图形的geojson数据
// 进入圆形模式。使用时是点击一下在地图上画一个默认半径的圆,选中后可以点边缘调整半径和中心点,如果发现点了一下什么都没发生的样子,可能只是你的地图zoom比较小,圆的半径太小了所以看不到
addCircle(cb) {
this._draw.changeMode("draw_circle");
this.initListener(cb);
// 进入自由多边形模式
addPolygon(cb) {
this._draw.changeMode("draw_polygon", {
// The id of the LineString to continue drawing
featureId: "",
// The point to continue drawing from
from: [],
this.initListener(cb);
// 外部传入geojson并添加到地图上
addGeoJson(json, cb) {
let featureIds = [];
if (Array.isArray(json)) {
json.forEach((item) => {
featureIds = featureIds.concat(this._draw.add(item));
} else {
featureIds = this._draw.add(json);
this.singleClear(featureIds);
this.initListener(cb);
stopDraw() {
this._draw.changeMode("simple_select", {
featureIds: [],
// 控制是否可编辑
enableEdit(enable) {
if (enable) {
this._draw.changeMode("simple_select", {
featureIds: [],
this.drawType = "single";
} else {
this._draw.changeMode("static");
this.drawType = "view";
// 单图形模式画完一个要清除之前画出来的图形
singleClear(thisIds) {
if (this.drawType === "single") {
let all = this._draw.getAll();
all.features.forEach((feature) => {
if (thisIds.indexOf(feature.id) === -1) {
this._draw.delete([feature.id]);
clearAll() {
this._draw.deleteAll();
画圆的默认半径优化,根据当前zoom改变
addCircle(cb) {
let scale = getScale();
this._draw.changeMode("draw_circle", { initialRadiusInKm: scale / 1000 });
this.initListener(cb);
* @description: 获取比例尺(整数缩放级别),即在纬度上每像素代表的实际距离
* @return {*}
// https://www.freesion.com/article/9003119133/
function getScale() {
let scale;
// let metersPerPixel = window.map.getProjection().getMetersPerPixelAtLatitude(window.map1.getCenter()); // 这个是参考例子给的方法但我使用时报错且解决不了
let metersPerPixel = 1;
let zoom = window.map.getZoom();
switch (Math.round(zoom)) {
case 2:
scale = 1000000 / metersPerPixel;
break;
case 3:
scale = 500000 / metersPerPixel;
break;
case 4:
scale = 200000 / metersPerPixel;
break;
case 5:
scale = 100000 / metersPerPixel;
break;
case 6:
scale = 50000 / metersPerPixel;
break;
case 7:
scale = 20000 / metersPerPixel;
break;
case 8:
scale = 10000 / metersPerPixel;
break;
case 9:
scale = 5000 / metersPerPixel;
break;
case 10:
scale = 2000 / metersPerPixel;
break;
case 11:
scale = 1000 / metersPerPixel;
break;
case 12:
scale = 500 / metersPerPixel;
break;
case 13:
scale = 200 / metersPerPixel;
break;
case 14:
scale = 100 / metersPerPixel;
break;
case 15:
scale = 50 / metersPerPixel;
break;
case 16:
scale = 20 / metersPerPixel;
break;
case 17:
scale = 10 / metersPerPixel;
break;
case 18:
scale = 5 / metersPerPixel;
break;
default:
break;
return scale;
const draw = new CustomDraw(); // 默认单图形可编辑模式
// const draw = new CustomDraw("view"); // 多图形仅查看模式
draw.clearAll(); // 清除
draw.enableEdit(false); // 停止编辑
// 进入对应模式,并获取画完后返回的数据
const addRectangle = () => {
draw.addRectangle(finishDraw);
const addCircle = () => {
draw.addCircle(finishDraw);
const addPolygon = () => {
draw.addPolygon(finishDraw);
// 画完以后返回feature的json数据
const finishDraw = (feature) => {
// 处理feature,比如显示在页面上
// 上传.json或.geojson格式的文件,读取并画在地图上。这里上传使用了element-plus的上传组件
const fileList = ref([]);
const handleUpload = (uploadFile) => {
loadJson(uploadFile.raw);
return false;
const loadJson = async (file) => {
const reader = new FileReader(); // 新建一个FileReader
reader.readAsText(file, "UTF-8"); // 读取文件
reader.onload = function (evt) {
// 读取完文件之后会回来这里
try {
let fileString = evt.target.result; // 读取文件内容
const jsonObj = JSON.parse(fileString);
// jsonObj 格式和作用类似上面 finishDraw 返回的 feature
draw.addGeoJson(jsonObj, finishDraw); // 这里添加监听是为了能够修改上传的形状
} catch (e) {
console.error("json读取或添加失败", e);
修改绘制的默认样式
官方主页下方有说明。在new MapboxDraw时的选项里添加styles:
styles: [
// ACTIVE (being drawn)
// line stroke
id: "gl-draw-line",
type: "line",
filter: ["all", ["==", "$type", "LineString"], ["!=", "mode", "static"]],
layout: {
"line-cap": "round",
"line-join": "round",
paint: {
"line-color": "#D20C0C",
"line-dasharray": [0.2, 2],
"line-width": 2,
// polygon fill
id: "gl-draw-polygon-fill",
type: "fill",
filter: ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
paint: {
"fill-color": "#D20C0C",
"fill-outline-color": "#D20C0C",
"fill-opacity": 0.1,
// polygon mid points
id: "gl-draw-polygon-midpoint",
type: "circle",
filter: ["all", ["==", "$type", "Point"], ["==", "meta", "midpoint"]],
paint: {
"circle-radius": 3,
"circle-color": "#fbb03b",
// polygon outline stroke
// This doesn't style the first edge of the polygon, which uses the line stroke styling instead
id: "gl-draw-polygon-stroke-active",
type: "line",
filter: ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
layout: {
"line-cap": "round",
"line-join": "round",
paint: {
"line-color": "#D20C0C",
"line-dasharray": [0.2, 2],
"line-width": 2,
// vertex point halos
id: "gl-draw-polygon-and-line-vertex-halo-active",
type: "circle",
filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
paint: {
"circle-radius": 5,
"circle-color": "#FFF",
// vertex points
id: "gl-draw-polygon-and-line-vertex-active",
type: "circle",
filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
paint: {
"circle-radius": 3,
"circle-color": "#D20C0C",
// INACTIVE (static, already drawn)
// line stroke
id: "gl-draw-line-static",
type: "line",
filter: ["all", ["==", "$type", "LineString"], ["==", "mode", "static"]],
layout: {
"line-cap": "round",
"line-join": "round",
paint: {
"line-color": "#ff0000",
"line-width": 3,
// polygon fill
id: "gl-draw-polygon-fill-static",
type: "fill",
filter: ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
paint: {
"fill-color": "#FF0000",
"fill-outline-color": "#000",
"fill-opacity": 0.23,
// polygon outline
id: "gl-draw-polygon-stroke-static",
type: "line",
filter: ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
layout: {
"line-cap": "round",
"line-join": "round",
paint: {
"line-color": "#E00000",
"line-width": 3,