---
title: 右键菜单
source: https://vjmap.com/guide/contextMenu.html
---

# 右键菜单

`ContextMenu` 上下文菜单用于右键弹出菜单界面显示。

## 常用方法

|方法|描述|
|:---|:---|
|add|增加菜单项|
|show|显示菜单 |
|hide|隐藏菜单|
|toggle|切换菜单状态|

## 菜单项类型
|类型|描述|
|:---|:---|
|seperator|分隔线|
|custom|自定义菜单项|
|multi|多个按钮|
|submenu|子菜单项|
|hovermenu|悬浮菜单项|
|normal|缺省正常菜单项|

## 示例

### 地图上下文菜单 

[地图上下文菜单](https://vjmap.com/demo/#/demo/map/contextmenu/01contextmenuMap)
```js
// 选中的图标，也可以直接指定src路径
const checkIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAANpJREFUOE9jZKAQMFKon2FgDKjZHVHf4rqiEeR6kl1QuzOyn+E/4/lmj2WLSDagekdkASMjg1aL+/I0WNgR7YLaHRG2/xgZYlrdV6QjBzzRBtTsijzzl4nVvd1l0VucBtTsjlz5/z/jt1a3ZYnIimp2RZ7//+9fQ6vHyo3o0Q53Qc32CAcGpv88DIxMxQyMDB9aXJcHghTX7IxcyMjAeLTZfdksbGkGqxdqdkUuZ2BgkPjPyDCFgeG/W6srqr+JCoPqXZG9jAwM9iyszy0aHA/8wZViiQ7EYWwAAPShQBF/1IlVAAAAAElFTkSuQmCC";
map.setMenu(event => {
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `<span style="color: #ffff00; padding-left: 30px">菜单右键功能演示</span>`},
            {type: 'multi', items: [
                    {label: '全图', onClick: () => { map.fitMapBounds(); }},
                    {label: '不旋转', onClick: () => { map.setBearing(0); }},
                    {label: '不倾斜', onClick: () => { map.setPitch(0); }},
                ]},
            {label: '获取此位置的坐标', onClick: () => {
                    let point = map.fromLngLat(event.lngLat);
                    map.logInfo(`当前坐标为 x: ${point.x}, y: ${point.y}`);
                }},
            {type: 'seperator'},
            {type: 'submenu', label: '地图缩放', items: [
                    {label: '放大一级', onClick: () => { map.setZoom(map.getZoom() + 1)}},
                    {label: '缩小一级', onClick: () => { map.setZoom(map.getZoom() - 1)}, enabled: map.getZoom() - 1 > 0},
                    {label: '缩小至最小级', onClick: () => { map.setZoom(0)}},
                    {label: '飞行至此位置', onClick: () => {
                            map.flyTo({
                                center: event.lngLat,
                                pitch: 60,
                                zoom: 5
                            })
                        }}
                ]},
            {type: 'hovermenu', label: '地图设置', items: [
                    {
                        label: '双击鼠标进行缩放',
                        icon: map.doubleClickZoom.isEnabled() ? checkIcon : "",
                        onClick: () => {
                            if (map.doubleClickZoom.isEnabled()) {
                                map.doubleClickZoom.disable()
                            } else {
                                map.doubleClickZoom.enable()
                            }
                        }
                    },
                    {
                        label: '允许地图旋转',
                        icon: map.dragRotate.isEnabled() ? checkIcon : "",
                        onClick: () => {
                            if (map.dragRotate.isEnabled()) {
                                map.dragRotate.disable();
                            } else {
                                map.dragRotate.enable();
                            }
                        }
                    },
                    {
                        label: '允许地图倾斜',
                        icon: map.getMaxPitch() != 0 ? checkIcon : "",
                        onClick: () => {
                            if (map.getMaxPitch() == 0) {
                                map.setMaxPitch(85)
                            } else {
                                map.setMaxPitch(0)
                            }
                        }
                    }
                ]}
        ]
    });
})
```

<mapFrameEx>
<pre>
// --地图上下文菜单--
// 地图服务对象
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打开地图
let res = await svc.openMap({
    mapid: env.exampleMapId, // 地图ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap.openMapDarkStyle() // div为深色背景颜色时，这里也传深色背景样式
})
if (res.error) {
    // 如果打开出错
    message.error(res.error)
}
// 根据地图范围建立几何投影坐标系
let prj = new vjmap.GeoProjection(res.bounds);
// 地图对象
let map = new vjmap.Map({
    container: 'map', // DIV容器ID
    style: svc.rasterStyle(), // 样式，这里是栅格样式
    center: prj.toLngLat(prj.getMapExtent().center()), // 设置地图中心点
    zoom: 2, // 设置地图缩放级别
    renderWorldCopies: false
});
// 关联服务对象和投影对象
map.attach(svc, prj);
let mapBounds = map.getGeoBounds(); // 得到地图地理范围
// 选中的图标，也可以直接指定src路径
const checkIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAANpJREFUOE9jZKAQMFKon2FgDKjZHVHf4rqiEeR6kl1QuzOyn+E/4/lmj2WLSDagekdkASMjg1aL+/I0WNgR7YLaHRG2/xgZYlrdV6QjBzzRBtTsijzzl4nVvd1l0VucBtTsjlz5/z/jt1a3ZYnIimp2RZ7//+9fQ6vHyo3o0Q53Qc32CAcGpv88DIxMxQyMDB9aXJcHghTX7IxcyMjAeLTZfdksbGkGqxdqdkUuZ2BgkPjPyDCFgeG/W6srqr+JCoPqXZG9jAwM9iyszy0aHA/8wZViiQ7EYWwAAPShQBF/1IlVAAAAAElFTkSuQmCC";
map.setMenu(event =&gt; {
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `&lt;span style="color: #ffff00; padding-left: 30px"&gt;菜单右键功能演示&lt;/span&gt;`},
            {type: 'multi', items: [
                    {label: '全图', onClick: () =&gt; { map.fitMapBounds(); }},
                    {label: '不旋转', onClick: () =&gt; { map.setBearing(0); }},
                    {label: '不倾斜', onClick: () =&gt; { map.setPitch(0); }},
                ]},
            {label: '获取此位置的坐标', onClick: () =&gt; {
                    let point = map.fromLngLat(event.lngLat);
                    map.logInfo(`当前坐标为 x: ${point.x}, y: ${point.y}`);
                }},
            {type: 'seperator'},
            {type: 'submenu', label: '地图缩放', items: [
                    {label: '放大一级', onClick: () =&gt; { map.setZoom(map.getZoom() + 1)}},
                    {label: '缩小一级', onClick: () =&gt; { map.setZoom(map.getZoom() - 1)}, enabled: map.getZoom() - 1 &gt; 0},
                    {label: '缩小至最小级', onClick: () =&gt; { map.setZoom(0)}},
                    {label: '飞行至此位置', onClick: () =&gt; {
                            map.flyTo({
                                center: event.lngLat,
                                pitch: 60,
                                zoom: 5
                            })
                        }}
                ]},
            {type: 'hovermenu', label: '地图设置', items: [
                    {
                        label: '双击鼠标进行缩放',
                        icon: map.doubleClickZoom.isEnabled() ? checkIcon : "",
                        onClick: () =&gt; {
                            if (map.doubleClickZoom.isEnabled()) {
                                map.doubleClickZoom.disable()
                            } else {
                                map.doubleClickZoom.enable()
                            }
                        }
                    },
                    {
                        label: '允许地图旋转',
                        icon: map.dragRotate.isEnabled() ? checkIcon : "",
                        onClick: () =&gt; {
                            if (map.dragRotate.isEnabled()) {
                                map.dragRotate.disable();
                            } else {
                                map.dragRotate.enable();
                            }
                        }
                    },
                    {
                        label: '允许地图倾斜',
                        icon: map.getMaxPitch() != 0 ? checkIcon : "",
                        onClick: () =&gt; {
                            if (map.getMaxPitch() == 0) {
                                map.setMaxPitch(85)
                            } else {
                                map.setMaxPitch(0)
                            }
                        }
                    }
                ]}
        ]
    });
})
map.logInfo('请点击右键弹出菜单');
</pre>
</mapFrameEx>
<br/>
<br/>


### 覆盖物图层右键菜单


[覆盖物图层右键菜单](https://vjmap.com/demo/#/demo/map/contextmenu/02contextmenuOverlay)

```js
// 设置点击一个覆盖物时弹出菜单,key值可以自定义设置。如果函数返回空时，将触发上面的地图右键菜单。如果函数不返回空，则会弹出此函数的菜单
map.setMenu(event => {
    let features = map.queryRenderedFeatures(event.point, {
        layers: [polygon.layerId]
    });
    if (features.length == 0) {
        // 如果点击位置没有上面多边形图层的实体，则将回空，这样将会弹出上面的默认地图菜单
        return null;
    }
    // 如果点击位置有上面多边形图层的实体，则弹出此图层的自定义右键菜单
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `<span style="color: #ffff00; padding-left: 30px">我是多边形图层的右键菜单</span>`},
            {
                label: '查看选择的多边形信息',
                onClick: () => {
                    map.logInfo(`当前多边形ID: ${features[0].properties.id}, 当前多边形颜色: ${features[0].properties.color}`);
                }
            },
            {
                label: '删除',
                onClick: () => {
                    let data = polygon.getData();
                    data.features = data.features.filter(f => f.id != features[0].properties.id);
                    polygon.setData(data);
                }
            }
        ]
    });
}, "overlay")
```

<mapFrameEx>
<pre>
// --覆盖物图层右键菜单--
// 地图服务对象
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打开地图
let res = await svc.openMap({
    mapid: env.exampleMapId, // 地图ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap.openMapDarkStyle() // div为深色背景颜色时，这里也传深色背景样式
})
if (res.error) {
    // 如果打开出错
    message.error(res.error)
}
// 根据地图范围建立几何投影坐标系
let prj = new vjmap.GeoProjection(res.bounds);
// 地图对象
let map = new vjmap.Map({
    container: 'map', // DIV容器ID
    style: svc.rasterStyle(), // 样式，这里是栅格样式
    center: prj.toLngLat(prj.getMapExtent().center()), // 设置地图中心点
    zoom: 2, // 设置地图缩放级别
    renderWorldCopies: false
});
// 关联服务对象和投影对象
map.attach(svc, prj);
await map.onLoad();
// 下面加很多多边形做测试,在多边形上右键时将弹出多边形的右键菜单，否则弹出地图的右键菜单
let mapBounds = map.getGeoBounds(0.6);
let len = mapBounds.width() / 50;
let polygons = []
for(let i = 0; i &lt; 200; i++) {
    const p1 = mapBounds.randomPoint();
    const p2 = vjmap.geoPoint([p1.x, p1.y + len]);
    const p3 = vjmap.geoPoint([p1.x + len, p1.y]);
    polygons.push({
        points: map.toLngLat([p1, p2, p3]),
        properties: {
            name: "polygon" + (i + 1),
            color: vjmap.randomColor()
        }
    })
}
let polygon = new vjmap.Polygon({
    data: polygons,
    // 如果是hover状态时，用红色，非hover状态时，取属性中的'color'做为颜色值
    fillColor: ['case', ['to-boolean', ['feature-state', 'hover']], 'red', ['get', 'color']],
    fillOpacity: 0.8,
    fillOutlineColor: "#f00",
    isHoverPointer: true,
    isHoverFeatureState: true
});
polygon.addTo(map);
polygon.clickLayer(e =&gt; map.logInfo(`您点击了第 ${e.features[0].id} 个，名称为 ${e.features[0].properties.name}，颜色为 ${e.features[0].properties.color} 的多边形`))
// 默认情况下显示的是地图菜单，因为这里的key是空，如果多次设置setMenu时，根据key值排序倒序，空值会在最后面
map.setMenu(event =&gt; {
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `&lt;span style="color: #ffff00; padding-left: 30px"&gt;我是地图的右键菜单&lt;/span&gt;`},
            {
                label: '全图范围显示',
                onClick: () =&gt; {
                    map.setBearing(0);
                    map.setPitch(0);
                    map.fitMapBounds();
                }
            },
            {
                label: '获取此位置的坐标',
                onClick: () =&gt; {
                    let point = map.fromLngLat(event.lngLat);
                    map.logInfo(`当前坐标为 x: ${point.x}, y: ${point.y}`);
                }
            }
        ]
    });
})
// 设置点击一个覆盖物时弹出菜单,key值可以自定义设置。如果函数返回空时，将触发上面的地图右键菜单。如果函数不返回空，则会弹出此函数的菜单
map.setMenu(event =&gt; {
    let features = map.queryRenderedFeatures(event.point, {
        layers: [polygon.layerId]
    });
    if (features.length == 0) {
        // 如果点击位置没有上面多边形图层的实体，则将回空，这样将会弹出上面的默认地图菜单
        return null;
    }
    // 如果点击位置有上面多边形图层的实体，则弹出此图层的自定义右键菜单
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `&lt;span style="color: #ffff00; padding-left: 30px"&gt;我是多边形图层的右键菜单&lt;/span&gt;`},
            {
                label: '查看选择的多边形信息',
                onClick: () =&gt; {
                    map.logInfo(`当前多边形ID: ${features[0].properties.id}, 当前多边形颜色: ${features[0].properties.color}`);
                }
            },
            {
                label: '删除',
                onClick: () =&gt; {
                    let data = polygon.getData();
                    data.features = data.features.filter(f =&gt; f.id != features[0].properties.id);
                    polygon.setData(data);
                }
            }
        ]
    });
}, "overlay")
// 绘图控件右键菜单
const draw = new vjmap.Draw.Tool();
map.addControl(draw, 'top-right');
map.on("draw.simpleselect.contextmenu", function (e) {
    // 如果不是此draw对象的事件，则不用管
    if (e.styleId != draw.options.styleId) return;
    let feature = e.event.featureTarget
    if (!feature) return
    //if (draw.getMode() != "simple_select") return
    let selectFeatureId = feature.properties.id
    // 需要选中的才能右键
    let selectIds = draw.getSelectedIds() || []
    if (selectIds.indexOf(selectFeatureId)== -1) return
    let event = e.event.originalEvent
    // 阻止地图默认右键菜单触发
    event.preventDefault();
    event.stopPropagation();
    return new vjmap.ContextMenu({
        event: event,
        theme: "dark", //light
        width: "250px",
        items: [
            {
                label: "删除",
                onClick: () =&gt; {
                    draw.delete(selectFeatureId)
                }
            }
        ]
    });
});
</pre>
</mapFrameEx>
<br/>
<br/>

### 标记Marker右键菜单

[标记Marker右键菜单](https://vjmap.com/demo/#/demo/map/contextmenu/03contextmenuMarker)
```js
 // marker与多边形等覆盖物图层不同的时，marker是div，而覆盖物图层是canvas渲染与map属于同一个div
// 所以marker的右键，得由自己的div的上下文菜单来触发
marker.getElement().addEventListener("mousedown", event => {
    if (event.button != 2) return;//不是右键
    // 阻止地图默认右键菜单触发
    event.preventDefault();
    event.stopPropagation();
    return new vjmap.ContextMenu({
        event: event,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `<span style="color: #ffff00; padding-left: 30px">我是标注Marker的右键菜单</span>`},
            {
                label: '获取此Marker的信息',
                onClick: () => {
                    map.logInfo(`当前MarkerId为 ${marker.id}`);
                }
            }
        ]
    });
})
```

<mapFrameEx>
<pre>
// --标记Marker右键菜单--
// 地图服务对象
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打开地图
let res = await svc.openMap({
    mapid: env.exampleMapId, // 地图ID
    mapopenway: vjmap.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap.openMapDarkStyle() // div为深色背景颜色时，这里也传深色背景样式
})
if (res.error) {
    // 如果打开出错
    message.error(res.error)
}
// 根据地图范围建立几何投影坐标系
let prj = new vjmap.GeoProjection(res.bounds);
// 地图对象
let map = new vjmap.Map({
    container: 'map', // DIV容器ID
    style: svc.rasterStyle(), // 样式，这里是栅格样式
    center: prj.toLngLat(prj.getMapExtent().center()), // 设置地图中心点
    zoom: 2, // 设置地图缩放级别
    renderWorldCopies: false
});
// 关联服务对象和投影对象
map.attach(svc, prj);
await map.onLoad();
map.setMenu(event =&gt; {
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `&lt;span style="color: #ffff00; padding-left: 30px"&gt;我是地图的右键菜单&lt;/span&gt;`},
            {
                label: '全图范围显示',
                onClick: () =&gt; {
                    map.setBearing(0);
                    map.setPitch(0);
                    map.fitMapBounds();
                }
            },
            {
                label: '获取此位置的坐标',
                onClick: () =&gt; {
                    let point = map.fromLngLat(event.lngLat);
                    app.logInfo(`当前坐标为 x: ${point.x}, y: ${point.y}`);
                }
            }
        ]
    });
})
let mapBounds = map.getGeoBounds(); // 得到地图地理范围
for(let i = 0; i &lt; 50; i++) {
    let point = mapBounds.randomPoint(); // 在地理范围里随机生成一个点
    // marker
    let marker = new vjmap.Marker({
        color: vjmap.randomColor()
    });
    marker.setLngLat(map.toLngLat(point))
    marker.addTo(map);
    marker.id = i + 1;
    // marker与多边形等覆盖物图层不同的时，marker是div，而覆盖物图层是canvas渲染与map属于同一个div
    // 所以marker的右键，得由自己的div的上下文菜单来触发
    marker.getElement().addEventListener("mousedown", event =&gt; {
        if (event.button != 2) return;//不是右键
        // 阻止地图默认右键菜单触发
        event.preventDefault();
        event.stopPropagation();
        return new vjmap.ContextMenu({
            event: event,
            theme: "dark", //light
            width: "250px",
            items: [
                {type: 'custom', markup: `&lt;span style="color: #ffff00; padding-left: 30px"&gt;我是标注Marker的右键菜单&lt;/span&gt;`},
                {
                    label: '获取此Marker的信息',
                    onClick: () =&gt; {
                        map.logInfo(`当前MarkerId为 ${marker.id}`);
                    }
                }
            ]
        });
    })
}
</pre>
</mapFrameEx>
<br/>
<br/>


# 类型定义文件

```ts
/**
 * 上下文菜单.
 *
 **/
export  class ContextMenu {
    private options;
    private menuControl;
    private position;
    /**
     * Creates a new ContextMenu menu
     * @param {object} opts options which build the menu e.g. position and items
     * @param {number} opts.width sets the width of the menu including children
     * @param {object} opts.event 事件对象
     * @param {theme} opts.theme 自定义的样式主题，支持dark和light，默认dark
     * @param {boolean} opts.isSticky sets how the menu apears, follow the mouse or sticky
     * @param {Array<ContextMenuItem>} opts.items sets the default items in the menu
     */
    constructor(opts: ContextMenuOptions);
    /**
     * Adds item to this ContextMenu menu instance
     * @param {ContextMenuItem} item item to add to the ContextMenu menu
     */
    add(item: any): void;
    /**
     * Makes this ContextMenu menu visible
     */
    show(): void;
    /**
     * Hides this ContextMenu menu
     */
    hide(): void;
    /**
     * Toggle visibility of menu
     */
    toggle(): void;
}

/**
 * 上下文菜单选项.
 *
 **/
export  interface ContextMenuOptions {
    /** 事件对象. */
    event: Event;
    /** 菜单宽度（包括子菜单),像素，默认150px. */
    width?: string;
    /** 菜单主题色.(dark和light,默认dark)*/
    theme?: string;
    /** 菜单外面容器的内部宽，用于菜单超时范围时自动调位置，默认是window大小宽 可通过 map.getContainer().getBoundingClientRect().width来设置 */
    innerWidth?: number;
    /** 菜单外面容器的内部宽，用于菜单超时范围时自动调位置，默认是window大小高 可通过 map.getContainer().getBoundingClientRect().height来设置 */
    innerHeight?: number;
    /** 子项 */
    items: ContextMenuSubItemOptions[];
}

/**
 * 上下文菜单子项选项.
 *
 **/
export  interface ContextMenuSubItemOptions {
    /** 子菜单类型. */
    type?: "custom" | "multi" | "Button" | "seperator" | "submenu" | "hovermenu" | "normal";
    /** 类型为'custom'时的自定义html内容 */
    markup?: string;
    /** 类型为'multi'时子菜单项*/
    items?: ContextMenuSubItemOptions[];
    /** 点击事件*/
    onClick?: Function;
    /** 菜单名称*/
    label?: string;
    /** 快捷菜单*/
    shortcut?: string;
    /** 是否能用*/
    enabled?: boolean;
    /** css图标*/
    cssIcon?: string;
    /** 图标*/
    icon?: string;
}

```