vue3 实现百度地图海量标记点绘制,百度地图加载海量点,vue3使用百度地图给海量点标记+定位搜索,百度地图海量撒点 +快速可视化渲染保证不卡速度快,渲染性能优化解决(万级数据)缓慢问题
1.首先,登录需选择个人或者企业实名认证进入百度地图开放平台,点击右上角的控制台,进入开发者管理界面:控制台 | 百度地图开放平台

方法一:由于marker标记很多,拖动时造成卡顿,需要优化(适用少量标点)



代码如下:
注意点: 记得换成自己的key
https://api.map.baidu.com/api?v=3.0&ak=你的key&callback=onBaiduMapLoaded
<template>
<div class="map-container">
<div id="container" class="map" tabindex="0"></div>
<div class="input-card">
<div class="mt5"><el-avatar :size="20" src="图标地址"/> 一级代理店</div>
<div class="mt5"><el-avatar :size="20" src="图标地址"/> 二级代理店</div>
<div class="mt5"><el-avatar :size="20" src="图标地址"/> 三级代理店</div>
</div>
<div class="info">
<div class="input-item">
<el-input v-model="addressInput">
<template #prepend>请输入地址</template>
</el-input>
</div>
<div class="input-item">
<el-input v-model="shopInput">
<template #prepend>请输入店号</template>
</el-input>
</div>
<el-button type='primary' @click="handleSearch">
点击搜索
</el-button>
</div>
<!-- 添加加载进度条 -->
<div class="loading-overlay" v-if="isLoading">
<div class="spinner"></div>
<h3 style="color: #409eff">正在加载代理店数据...</h3>
<p style="color: #606266">已加载 {{ loadedCount }} / {{ totalCount }} 家代理店 ({{ Math.round(progress) }}%)</p>
<el-progress :percentage="Math.round(progress)" status="success" :stroke-width="6"></el-progress>
</div>
</div>
</template>
<script>
import { onMounted, ref } from 'vue';
// import { Api } from "../manage/api";
export default {
name: 'AgentMapBaidu',
setup() {
const addressInput = ref('');
const shopInput = ref('');
const isLoading = ref(true);
const progress = ref(0);
const loadedCount = ref(0);
const totalCount = ref(0);
let map = null;
let markers = [];
let localSearch = null;
let baiduApiLoaded = false;
let shopData = []; // 存储所有店铺数据
let batchIndex = 0; // 当前批次索引
const batchSize = 100; // 减少每批加载的数量
const delayBetweenMarkers = 10; // 每个标记点之间的延迟(ms)
// 百度地图点样式
const getPointStyles = () => {
if (!window.BMap) return null;
return {
'一级代理店': {
color: '#fcb715',
icon: '图标地址',
size: new BMap.Size(50, 50),
anchor: new BMap.Pixel(5, 5),
opacity: 0.1
},
'二级代理店': {
color: '#00862b',
icon: '图标地址',
size: new BMap.Size(50, 50),
anchor: new BMap.Pixel(5, 5),
opacity: 0.1
},
'三级代理店': {
color: '#015aef',
icon: '图标地址',
size: new BMap.Size(50, 50),
anchor: new BMap.Pixel(5, 5),
opacity: 0.1
}
};
};
const initBaiduApi = () => {
return new Promise((resolve, reject) => {
if (window.BMap) {
resolve();
return;
}
// 创建脚本标签
const script = document.createElement('script');
script.src = `https://api.map.baidu.com/api?v=3.0&ak=你的key&callback=onBaiduMapLoaded`;
script.async = true;
// 全局回调函数
window.onBaiduMapLoaded = () => {
baiduApiLoaded = true;
resolve();
};
script.onerror = () => {
console.error('百度地图API加载失败');
reject(new Error('百度地图API加载失败'));
};
document.head.appendChild(script);
});
};
const loadMap = () => {
if (!window.BMap) {
console.error('BMap is not defined');
return;
}
map = new BMap.Map("container");
const point = new BMap.Point(102.342785, 35.312316);
map.centerAndZoom(point, 5);
map.enableScrollWheelZoom();
// 初始化本地搜索
localSearch = new BMap.LocalSearch(map, {
renderOptions: { map: map, panel: null },
pageCapacity: 1
});
// 加载代理店数据
loadAgentData();
map.addEventListener("click", function(e) {
console.log(e.point.lng + "," + e.point.lat);
});
};
// 加载所有代理店数据
const loadAgentData = async () => {
if (!window.BMap) return;
try {
// 获取所有店铺数据
// shopData = await Api().GetALl({fcode: '', faddr: '', State: true}); // 此处请替换为实际接口
shopData = [ { FDLDName: "梁xx", FAdr: "四川省西昌市宁远桥光xxx", FDLDCode2: "0376AO", FType: "二级代理店", FLong: "102.2518450000", FLat: "27.8949840000", FId: 1301} ]
// shopData = shopData.slice(0, 2000);
// 设置总数量
totalCount.value = shopData.length;
// 清除旧标记
clearMarkers();
// 开始分批加载
await loadBatch();
} catch (error) {
console.error('加载代理店数据失败:', error);
isLoading.value = false;
}
};
// 分批加载数据(优化版)
const loadBatch = async () => {
if (!window.BMap) return;
const start = batchIndex * batchSize;
const end = Math.min(start + batchSize, totalCount.value);
const pointStyles = getPointStyles();
if (!pointStyles) return;
// 处理当前批次的数据(添加延迟)
for (let i = start; i < end; i++) {
await processMarker(i, pointStyles);
// 每处理10个标记点更新一次进度
if ((i - start) % 10 === 0) {
loadedCount.value = i + 1;
progress.value = ((i + 1) / totalCount.value) * 100;
}
}
// 更新加载状态
loadedCount.value = end;
progress.value = (end / totalCount.value) * 100;
// 处理下一批次
if (end < totalCount.value) {
batchIndex++;
// 批次之间添加更长的延迟
await new Promise(resolve => setTimeout(resolve, 10));
loadBatch();
} else {
// 所有数据加载完成
isLoading.value = false;
}
};
// 处理单个标记点(添加延迟)
const processMarker = async (index, pointStyles) => {
const shop = shopData[index];
const lng = parseFloat(shop.FLong) || 0;
const lat = parseFloat(shop.FLat) || 0;
// 跳过无效坐标
if (lng === 0 || lat === 0) {
return;
}
const point = new BMap.Point(lng, lat);
let shopType = '一级代理店';
if (shop.FType === '二级代理店') {
shopType = '二级代理店';
} else if (shop.FType === '三级代理店') {
shopType = '三级代理店';
}
const style = pointStyles[shopType];
if (!style) {
return;
}
// 创建图标
const markerIcon = new BMap.Icon(style.icon, style.size, {
imageOffset: style.offset,
imageSize: style.size
});
const marker = new BMap.Marker(point, {
icon: markerIcon,
enableMassClear: true,
enableDragging: false,
opacity: style.opacity || 1
});
const infoContent = `
<div style="${'padding: 5px;min-width: 150px;color: ' + style.color};">
<div>店号:${shop.FDLDCode2 || '无'}</div>
<div>店主:${shop.FDLDName || '无'}</div>
<div>地址:${shop.FAdr || '无'}</div>
<div>类型:${shop.FType || '无'}</div>
</div>
`;
const infoWindow = new BMap.InfoWindow(infoContent);
marker.addEventListener("mouseover", function() {
this.openInfoWindow(infoWindow);
});
map.addOverlay(marker);
markers.push(marker);
// 添加延迟,让浏览器有时间处理UI
await new Promise(resolve => setTimeout(resolve, delayBetweenMarkers));
};
const clearMarkers = () => {
if (!map || markers.length === 0) return;
markers.forEach(marker => {
map.removeOverlay(marker);
});
markers = [];
console.log("清除所有标记");
};
const handleSearch = async() => {
if (!window.BMap) {
console.error('BMap is not defined');
return;
}
if (addressInput.value) {
localSearch.search(addressInput.value);
return;
}
if (shopInput.value) {
// let arr = await Api().GetALl({fcode: shopInput.value, faddr: '', State: true}); //此处请替换为实际接口地址
let arr = [ { FDLDName: "梁xx", FAdr: "四川省西昌市宁远桥光xxx", FDLDCode2: "0376AO", FType: "二级代理店", FLong: "102.2518450000", FLat: "27.8949840000", FId: 1301} ]
console.log(arr[0]);
if( arr.length > 0){
localSearch.search(arr[0].FAdr);
console.log(arr[0].FAdr);
}
}
};
const clearShopInput = () => shopInput.value = '';
const clearAddressInput = () => addressInput.value = '';
onMounted(async () => {
try {
await initBaiduApi();
loadMap();
} catch (error) {
console.error('地图初始化失败:', error);
isLoading.value = false;
}
});
return {
addressInput,
shopInput,
handleSearch,
clearShopInput,
clearAddressInput,
isLoading,
progress,
loadedCount,
totalCount
};
}
};
</script>
<style>
html, body, #container {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
.map-container {
position: relative;
height: 100vh;
width: 100vw;
}
.input-card {
position: absolute;
bottom: 100px;
right: 20px;
background: white;
padding: 10px;
border-radius: 4px;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
z-index: 10;
}
.info {
position: absolute;
top: 20px;
right: 20px;
background: white;
padding: 10px;
border-radius: 4px;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
z-index: 10;
}
.input-item {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.input-item-prepend {
margin-right: 10px;
}
.mini-button {
background: #4a90e2;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
}
.mini-button:hover {
background: #3a7bc8;
}
.icon-search {
margin-right: 5px;
}
/* 加载覆盖层样式 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.85);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
backdrop-filter: blur(3px);
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(64, 158, 255, 0.2);
border-radius: 50%;
border-top-color: #409eff;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
测试只是生成了1000个标点。
接下来要测试下2000个点,嗯有点小卡,但是还能操作,
5000个点试试,完美,动也动不了 简直漂亮
10000个点,芭比Q页面无响应浏览器卡死~~~
方法二:(解决万级标点渲染,结合echarts使用所需插件自行安装)
npm install echarts --save
测试25600+条标点~嗯秒出...还可以

测试一下搜索地址和店号定位标点~不错也是秒出~完结散花~~


代码如下:
核心代码:
const initMap = () => {
chartInstance = echarts.init(document.getElementById('map-chart'));
// 初始配置 - 简化地图样式配置
const option = {
animation: true,
tooltip: {
trigger: 'item',
formatter: params => {
if (params.data) {
const data = params.data;
return `
<div style="padding: 5px; min-width: 150px; color: ${shopTypeConfig[data.type]?.color || '#333'}">
<div>店号:${data.code || '无'}</div>
<div>店主:${data.name || '无'}</div>
<div>地址:${data.address || '无'}</div>
<div>类型:${data.type || '无'}</div>
</div>
`;
}
return '';
}
},
bmap: {
center: [102.342785, 35.312316],
zoom: 5,
roam: true,
// 移除自定义样式,使用默认样式
mapStyle: {}
},
series: [{
name: '代理店',
type: 'scatter',
coordinateSystem: 'bmap',
data: [],
symbolSize: function(val) {
return shopTypeConfig[val.type]?.symbolSize || 10;
},
itemStyle: {
color: function(val) {
return shopTypeConfig[val.data?.type]?.color || '#333';
}
},
emphasis: {
itemStyle: {
color: function(val) {
return shopTypeConfig[val.data?.type]?.color || '#333';
},
borderWidth: 2,
borderColor: '#fff'
}
}
}]
};
chartInstance.setOption(option);
// 获取百度地图实例
bmap = chartInstance.getModel().getComponent('bmap').getBMap();
bmap.enableScrollWheelZoom();
// 初始化本地搜索
localSearch = new BMap.LocalSearch(bmap, {
renderOptions: { map: bmap, panel: null },
pageCapacity: 1
});
loadAgentData();
};
<template>
<div class="map-container">
<div id="map-chart" class="map" tabindex="0"></div>
<div class="input-card">
<div class="mt5"><el-avatar :size="20" src="图标地址"/>
一级代理店
</div>
<div class="mt5"><el-avatar :size="20" src="图标地址"/>
二级代理店
</div>
<div class="mt5"><el-avatar :size="20" src="图标地址"/>
三级代理店
</div>
</div>
<div class="info">
<div class="input-item">
<el-input @keyup.enter="handleSearch" @input="getaddValue" v-model="addressInput">
<template #prepend>请输入地址</template>
</el-input>
</div>
<div class="input-item">
<el-input @keyup.enter="handleSearch" @input="getaddFcode" v-model="shopInput">
<template #prepend>请输入店号</template>
</el-input>
</div>
<el-button type='primary' @click="handleSearch">
点击搜索
</el-button>
</div>
</div>
</template>
<script>
import { onMounted, ref, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts';
import 'echarts/extension/bmap/bmap';
export default {
name: 'AgentMapEcharts',
setup() {
const addressInput = ref('');
const shopInput = ref('');
const flageone = ref(false)
const flagetwo = ref(false)
let chartInstance = null;
let bmap = null;
let localSearch = null;
// 店铺类型配置
const shopTypeConfig = {
'一级代理店': {
color: '#fcb715',
icon: '图标地址',
symbolSize: 18 //图标在地图大小显示
},
'二级代理店': {
color: '#00862b',
icon: '图标地址',
symbolSize: 16
},
'三级代理店': {
color: '#015aef',
icon: '图标地址',
symbolSize: 16
}
};
// 初始化地图和图表
const initMap = () => {
chartInstance = echarts.init(document.getElementById('map-chart'));
// 初始配置 - 简化地图样式配置
const option = {
animation: true,
tooltip: {
trigger: 'item',
formatter: params => {
if (params.data) {
const data = params.data;
return `
<div style="padding: 5px; min-width: 150px; color: ${shopTypeConfig[data.type]?.color || '#333'}">
<div>店号:${data.code || '无'}</div>
<div>店主:${data.name || '无'}</div>
<div>地址:${data.address || '无'}</div>
<div>类型:${data.type || '无'}</div>
</div>
`;
}
return '';
}
},
bmap: {
center: [102.342785, 35.312316],
zoom: 5,
roam: true,
// 移除自定义样式,使用默认样式
mapStyle: {}
},
series: [{
name: '代理店',
type: 'scatter',
coordinateSystem: 'bmap',
data: [],
symbolSize: function(val) {
return shopTypeConfig[val.type]?.symbolSize || 10;
},
itemStyle: {
color: function(val) {
return shopTypeConfig[val.data?.type]?.color || '#333';
}
},
emphasis: {
itemStyle: {
color: function(val) {
return shopTypeConfig[val.data?.type]?.color || '#333';
},
borderWidth: 2,
borderColor: '#fff'
}
}
}]
};
chartInstance.setOption(option);
// 获取百度地图实例
bmap = chartInstance.getModel().getComponent('bmap').getBMap();
bmap.enableScrollWheelZoom();
// 初始化本地搜索
localSearch = new BMap.LocalSearch(bmap, {
renderOptions: { map: bmap, panel: null },
pageCapacity: 1
});
loadAgentData();
};
// 加载代理店数据
const loadAgentData = async () => {
try {
// let shops = await Api().GetALl({fcode: '', faddr: '', State: true}); // 此处请替换为实际接口地址
let shops = [ { FDLDName: "梁xx", FAdr: "四川省西昌市宁远桥光xxx", FDLDCode2: "0376AO", FType: "二级代理店", FLong: "102.2518450000", FLat: "27.8949840000", FId: 1301} ]
// console.log("店铺数据:", shops);
// 转换数据格式
const data = shops
.filter(shop => {
const lng = parseFloat(shop.FLong) || 0;
const lat = parseFloat(shop.FLat) || 0;
return lng !== 0 && lat !== 0;
})
.map(shop => {
// if( shop.FType === '三级代理店'){
// console.log("==>", shop);
// }
const type = shop.FType || '一级代理店';
return {
name: shop.FDLDName,
code: shop.FDLDCode2,
address: shop.FAdr,
type: type,
value: [parseFloat(shop.FLong), parseFloat(shop.FLat), shop.FDLDName],
symbol: `image://${shopTypeConfig[type]?.icon}`,
symbolSize: shopTypeConfig[type]?.symbolSize || 10,
itemStyle: {
color: shopTypeConfig[type]?.color || '#333'
}
};
});
// 更新图表数据
chartInstance.setOption({
series: [{
data: data
}]
});
} catch (error) {
console.error('加载代理店数据失败:', error);
}
};
const getaddValue = () => {
flageone.value = true
flagetwo.value = false
};
const getaddFcode = () => {
flagetwo.value = true
flageone.value = false
};
// 处理搜索
const handleSearch = async () => {
if (!bmap) return;
if (addressInput.value && flageone.value) {
localSearch.search(addressInput.value);
shopInput.value = ''
return;
}
if (shopInput.value && flagetwo.value) {
// let arr = await Api().GetALl({fcode: shopInput.value, faddr: '', State: true}); // 此处请替换为实际接口地址
let arr = [ { FDLDName: "梁xx", FAdr: "四川省西昌市宁远桥光xxx", FDLDCode2: "0376AO", FType: "二级代理店", FLong: "102.2518450000", FLat: "27.8949840000", FId: 1301} ]
if (arr.length > 0) {
localSearch.search(arr[0].FAdr);
addressInput.value = ''
}
}
};
// 窗口大小变化时重新调整图表大小
const handleResize = () => {
if (chartInstance) {
chartInstance.resize();
}
};
onMounted(() => {
// 检查是否已加载百度地图API
if (window.BMap) {
initMap();
return;
}
// 动态加载百度地图API
const script = document.createElement('script');
script.src = `https://api.map.baidu.com/api?v=3.0&ak=你的key&callback=initBMapCallback`; //需要换成百度地图的key
script.async = true;
window.initBMapCallback = () => {
initMap();
};
script.onerror = () => {
console.error('百度地图API加载失败');
};
document.head.appendChild(script);
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
if (chartInstance) {
chartInstance.dispose();
}
});
return {
addressInput,
shopInput,
handleSearch,
getaddValue,
getaddFcode
};
}
};
</script>
<style>
.map-container {
position: relative;
height: 100vh;
width: 100vw;
}
.map {
height: 100%;
width: 100%;
}
.input-card {
position: absolute;
bottom: 100px;
right: 20px;
background: white;
padding: 10px;
border-radius: 4px;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
z-index: 10;
}
.info {
position: absolute;
top: 20px;
right: 20px;
background: white;
padding: 10px;
border-radius: 4px;
box-shadow: 0 2px 6px 0 rgba(114, 124, 245, .5);
z-index: 10;
}
.input-item {
display: flex;
align-items: center;
margin-bottom: 10px;
}
</style>
有问题欢迎来聊~需要离线版本的可以@@
更多推荐
所有评论(0)