1、参考文章:vue+mapboxgl 鹰眼展示_mapbox鹰眼图-CSDN博客
2、讲解:主要是基于mapbox来实现的功能
3、我这个是基于离线操作来实现的,地图不需要token
4、具体代码
<div ref="mapContainer" class="map-container" id="map">
<!-- 鹰眼视图 -->
<div style="position: absolute;bottom:0px;right: 0px;z-index: 200;width: 308px;height:208px;"
v-show="isOverviewVisible">
<div id="overview" class="overview" style="width:300px;height:200px;margin: 3px 3px 3px 3px;">
</div>
</div>
<!-- 视图收起与展开 -->
<div class="overview-toggle" @click="toggleOverview">
<el-icon :size="24">
<template v-if="isOverviewVisible">
<BottomRight />
</template>
<template v-else>
<TopLeft />
</template>
</el-icon>
</div>
</div>
const isOverviewVisible = ref(true);
let data = {}; // 初始为空对象
const toggleOverview = () => {
isOverviewVisible.value = !isOverviewVisible.value;
if (isOverviewVisible.value) {
// 展开鹰眼视图
document.getElementById('overview').style.display = 'block';
overviewMap.resize(); // 调整鹰眼视图的大小
// 使用 once 确保在地图渲染完成后再更新
overviewMap.once('render', () => {
syncMapPositions(false); // 同步视图
});
} else {
// 收起鹰眼视图
document.getElementById('overview').style.display = 'none';
}
};
const eagleEyeView = () => {
if (data) {
// 初始化鹰眼图
overviewMap = new mapboxgl.Map({
container: 'overview',
style: data,
center: map.getCenter(),
zoom: Math.max(5, map.getZoom() - 3), // 确保最小缩放级别
pitch: 0, // 鹰眼图保持俯瞰视角
bearing: 0, // 鹰眼图不旋转
interactive: true, // 启用交互
attributionControl: false
});
// 主地图 -> 鹰眼视图同步
map.on('move', () => syncMapPositions(false));
map.on('zoom', () => syncMapPositions(false));
map.on('rotate', () => syncMapPositions(false));
map.on('pitch', () => syncMapPositions(false));
// 鹰眼视图 -> 主地图同步
overviewMap.on('moveend', () => syncMapPositions(true));
overviewMap.on('zoomend', () => syncMapPositions(true));
// 点击鹰眼视图时更新主地图
overviewMap.on('click', (e) => {
if (isOverviewVisible.value) {
updateMainMap(e.lngLat, overviewMap.getZoom() + 3);
}
});
overviewMap.on('load', () => {
// 添加矩形框
const extentRectangleId ='main-map-extent';
// 添加数据源
overviewMap.addSource(extentRectangleId, {
type: 'geojson',
data: {
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: []
}
}
});
// 添加图层
overviewMap.addLayer({
id: extentRectangleId,
type: 'line',
source: extentRectangleId,
paint: {
'line-color': '#ff0000',
'line-width': 2,
'line-dasharray': [4, 2]
}
});
overviewMap.once('render', () => {
overviewMap.setStyle(map.getStyle());
syncMapPositions(false);
});
});
}
}
// 避免循环更新的标志
let isSyncing = false;
// 同步地图位置的通用方法
const syncMapPositions = (fromOverview) => {
if (isSyncing || !map || !overviewMap) return;
isSyncing = true;
try {
if (fromOverview) {
// 鹰眼视图 -> 主地图
updateMainMap(overviewMap.getCenter(), overviewMap.getZoom() + 3);
} else {
// 主地图 -> 鹰眼视图
const mainBounds = map.getBounds();
// 更新鹰眼视图中的可视区域多边形
const extentRectangleId ='main-map-extent';
if (overviewMap.getSource('main-map-extent')) {
overviewMap.getSource('main-map-extent').setData({
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[mainBounds.getSouthWest().lng, mainBounds.getNorthEast().lat],
[mainBounds.getNorthEast().lng, mainBounds.getNorthEast().lat],
[mainBounds.getNorthEast().lng, mainBounds.getSouthWest().lat],
[mainBounds.getSouthWest().lng, mainBounds.getSouthWest().lat],
[mainBounds.getSouthWest().lng, mainBounds.getNorthEast().lat]
]
]
}
});
}
// 更新鹰眼视图的位置和缩放
overviewMap.jumpTo({
center: map.getCenter(),
zoom: Math.max(5, map.getZoom() - 3),
bearing: map.getBearing(),
pitch: 0,
duration: 0
});
}
} catch (error) {
console.error('同步地图位置时出错:', error);
} finally {
// 使用setTimeout确保异步执行,避免UI阻塞
setTimeout(() => {
isSyncing = false;
}, 100);
}
};
// 更新主地图位置
const updateMainMap = (center, zoom) => {
// 限制最大和最小缩放级别
const clampedZoom = Math.min(Math.max(zoom, 5), 22);
map.flyTo({
center: center,
zoom: clampedZoom,
bearing: 0,
pitch: 0,
duration: 500
});
};
const initialization = () => {
let bounds = [
[103.500206757969, 24.6213633798346], // 左下角经纬度
[109.750964369846, 29.2243422652764] // 右上角经纬度
];
const glyphsUrl = '../../../static/Open Sans Semibold,Arial Unicode MS Bold/{fontstack}/{range}.pbf';
baseService.get("/api/qjl/tile/json").then(response => {
if (response) {
// 将后端返回的数据赋值给 data
data = response.data;
// 动态添加 glyphs 字段
data.glyphs = glyphsUrl;
// 在数据准备好之后初始化地图
map = new mapboxgl.Map({
container: mapContainer.value,
style: data,
pitch: 0, // 初始倾斜角度
bearing: 0, // 初始旋转角度
dragRotate: true, // 启用拖动旋转
touchRotate: true, // 启用触摸旋转
dragPan: true, // 启用拖动平移
scrollZoom: true, // 启用滚轮缩放
touchZoomRotate: true, // 启用触摸缩放和旋转
zoom: 5, // 初始层级
minZoom: 5, // 最小缩放级别
maxZoom: 22, // 最大缩放级别
center: [106.7132, 26.5728], // 贵州省中心点
maxBounds: bounds, // 设置地图的拖动范围
});
map.on('load', () => {
//绘制图斑
// 初始化Mapbox GL Draw插件
draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
polygon: false,
trash: false,
combine_features: true,
uncombine_features: true,
},
});
map.addControl(draw)
//初始化贵州边界
loadGuizhouBoundaries()
// initializeDrawPlugin()
//点击展示图斑信息
// clickPolygon()
bindClickEventsToLayers()
//初始化鹰眼图
eagleEyeView()
});
} else {
console.error("No data received from the backend.");
}
}).catch(error => {
console.error("Failed to fetch data from the backend:", error);
});
}
/*鹰眼视图控制按钮样式 */
.overview-toggle {
position: absolute;
bottom: -8px;
right: -8px;
z-index: 201;
width: 36px;
height: 36px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
transition: background-color 0.3s;
}
.overview-toggle:hover {
background-color: rgba(0, 0, 0, 0.9);
}
主要是实现鹰眼功能,所有其他代码有些可以不需要
5、具体效果