高德地图 Vue3 常用操作,点、聚合、线、移动、点击事件等
初始化地图,可在设置地图样式、中心点、缩放等
let map = null;
onMounted(() => {
initMap();
}
const initMap = () => {
AMapLoader.load({
key: "xxx", // 申请好的Web端开发者Key,首次调用 load 时必填
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.MoveAnimation', 'AMap.MarkerCluster'] // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap) => {
map = new AMap.Map('container1', {
//设置地图容器id
mapStyle: 'amap://styles/ca482dc75f8d435993e158d63b3f8695',
viewMode: '2D', //是否为3D地图模式
zoom: 14, //初始化地图级别
// center: [120.576963, 30.048733] //初始化地图中心点位置
center: [120.876508, 30.044303]
});
map.setDefaultCursor('pointer');
// 根据需要创建图标(用于非聚合点位)
stationIcon = new AMap.Icon({
// 图标尺寸
size: new AMap.Size(33, 49.9),
// 图标的取图地址
image: stationPointImg,
// 图标所用图片大小
imageSize: new AMap.Size(33, 49.9),
// 图标取图偏移量
imageOffset: new AMap.Pixel(0, 0)
});
})
};
<div id="container1" style="width: 100%; height: 100%"></div>
创建非聚合点位,设置点位图标、点击显示详情窗口、清除点位
function updateStationPoint(data) {
stationMarkers = []
data.forEach(item => {
let marker = new AMap.Marker({
map: map,
position: [item.lon, item.lat],
offset: new AMap.Pixel(-16, 0),
icon: stationIcon,
data: item,
});
marker.on('click', () => {
showBusInfoWindow(marker._originOpts.data)
});
stationMarkers.push(marker);
})
map.add(stationMarkers)
}
const showBusInfoWindow = async (item) => {
// 根据 item.type 动态选择组件
let ComponentToMount;
let siteInfo = null;
let routeInfo = null;
if (item.pointType === 'bus') {
ComponentToMount = MapBusTooltip;
} else if (item.pointType === 'site') {
ComponentToMount = MapSiteTooltip;
siteInfo = await queryBasicStationInfoById(item.id);
} else if (item.pointType === 'station') {
ComponentToMount = MapStationTooltip;
} else if (item.pointType === 'line') {
ComponentToMount = MapLineTooltip;
routeInfo = await RouteApi.queryBasicRouteInfoByRouteCode(item.routeCode);
}
// 创建一个挂载点
const mountPoint = document.createElement('div');
// 挂载Vue组件
const app = createApp(ComponentToMount, {
item: item,
siteInfo: siteInfo,
routeInfo: routeInfo,
methods: methods // 方法作为响应式对象传递
}
);
app.mount(mountPoint);
// 创建信息窗体并设置内容
infoWindow = new AMap.InfoWindow({
isCustom: true,
content: mountPoint,
offset: new AMap.Pixel(0, -0)
});
// 在地图上打开信息窗体
infoWindow.open(map, [item.lon, item.lat]);
const closeButton = document.querySelector('.close-button');
if (closeButton) {
closeButton.addEventListener('click', () => {
infoWindow.close();
});
}
// 在不需要时卸载组件
infoWindow.on('close', () => {
app.unmount();
});
}
// 清除场站点位
function clearBusStationPoint() {
map.remove(stationMarkers);
}
创建线条并让点跟随线条移动,调节移动速度
import busImg from '@/assets/imgs/massTransitMap/bus.svg';
import {getGpsDeTrail} from "@/api/bus/supervise";
const busTrajectoryRef = ref(null);
// 全局变量保存当前查看轨迹的线路和点位的引用
let currentPolyline = null;
let currentMarker = null;
let currentTrail = []; // 存储当前轨迹数组
let currentSpeed = 10000; // 初始速度
let showTrajectory = ref(false);
let nowPlateNum = '';
// 方法作为响应式对象传递给子组件
const methods = reactive({
parentMethodShowTrajectory: async (plateNum) => {
console.log(plateNum);
console.log(nowPlateNum);
if (plateNum) {
nowPlateNum = plateNum;
} else {
plateNum = nowPlateNum;
}
showTrajectory.value = true;
let dateRange = busTrajectoryRef.value.dateRange;
// 获取轨迹
let data = await getGpsDeTrail(plateNum, busTrajectoryformatDateTime(dateRange.start), busTrajectoryformatDateTime(dateRange.end));
console.log(data);
currentTrail = data.map(item => [item.lon, item.lat]);
if (currentTrail.length > 0) {
// 清除现有的线路和点位
// clearMapElements();
// closeInfoWindow();
// clearBusEnterprisePoint();
// 创建新的线路
currentPolyline = new AMap.Polyline({
map: map,
path: currentTrail,
showDir: true,
strokeColor: '#28F',
strokeWeight: 6
});
map.add(currentPolyline);
// 创建新的点位
currentMarker = new AMap.Marker({
map: map,
position: currentTrail[0],
icon: busImg,
offset: new AMap.Pixel(-13, -30)
});
// 移动结束事件
currentMarker.on('moveend', function (e) {
console.log("Movement finished");
});
map.setCenter(currentTrail[0], true);
} else {
console.log('No valid GPS data available.');
}
},
});
function busTrajectoryformatDateTime(dateString) {
const date = new Date(dateString);
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
}
// 开始播放
async function startMovement() {
// 动画移动标记
currentMarker.moveAlong(currentTrail, {
speed: currentSpeed,
delay: 0
});
}
// 暂停
function pauseMovement() {
currentMarker.stopMove(); // 暂停移动
}
// 继续
function continueMovement() {
currentMarker.resumeMove();
}
// 调节速度
function adjustSpeed(newSpeed) {
currentSpeed = newSpeed;
if (currentMarker) {
currentMarker.moveAlong(currentTrail, {
speed: currentSpeed,
delay: 0
});
}
}
创建聚合点位,可自定义聚合样式
pointsCruiseTaxi、pointsRideHailingTaxi分开存放聚合点位,即可不同类型的点聚合到不同的聚合中
这里我初始化全部数据是用于其他全局搜索功能
// 巡游出租车、网约车数量
const taxiCount1 = ref(0);
const taxiCount2 = ref(0);
// 巡游出租车点位
let allCruiseTaxiPointData = ref([])
let cruiseTaxiPointData = ref([])
let clusterCruiseTaxi = null;
let pointsCruiseTaxi = [];
let cruiseTaxiCount = ref(pointsCruiseTaxi.length);
// 网约车点位
let allRideHailingTaxiPointData = ref([])
let rideHailingTaxiPointData = ref([])
let clusterRideHailingTaxi = null;
let pointsRideHailingTaxi = [];
let rideHailingTaxiCount = ref(pointsRideHailingTaxi.length);
async function updateCruiseTaxiData() {
const data = await ApiTaxi.taxiCruisePoint();
taxiCount1.value = data.length;
// console.log(data)
// 初始化全部数据
if (allCruiseTaxiPointData.value.length === 0) {
allCruiseTaxiPointData.value = data.map(item => {
// 这里做了一次经纬度84转高德
let gcj02 = wgs84togcj02(item.lon, item.lat);
return {
...item, // 保持其他属性不变
lon: gcj02[0],
lat: gcj02[1]
};
});
}
cruiseTaxiPointData.value = data.map(item => {
let gcj02 = wgs84togcj02(item.lon, item.lat);
return {
...item,
lon: gcj02[0],
lat: gcj02[1]
};
});
}
function updateCruiseTaxiPoint(data) {
data.forEach(item => {
pointsCruiseTaxi.push(
{
lnglat: [item.lon, item.lat],
pointType: 'cruiseTaxi',
...item
}
);
})
cruiseTaxiCount.value = pointsCruiseTaxi.length; // 更新count以反映最新的点数量
addCluster('cruiseTaxi');
}
function clearCruiseTaxiPoint() {
pointsCruiseTaxi = [];
if (clusterCruiseTaxi) {
clusterCruiseTaxi.setMap(null); // 清除聚合点
}
return;
}
async function updateRideHailingTaxiData() {
const data = await ApiTaxi.taxiRideHailingPoint();
taxiCount2.value = data.length;
// 初始化全部数据
if (allRideHailingTaxiPointData.value.length === 0) {
allRideHailingTaxiPointData.value = data.map(item => {
let gcj02 = wgs84togcj02(item.lon, item.lat);
return {
...item, // 保持其他属性不变
lon: gcj02[0],
lat: gcj02[1]
};
});
}
rideHailingTaxiPointData.value = data.map(item => {
let gcj02 = wgs84togcj02(item.lon, item.lat);
return {
...item,
lon: gcj02[0],
lat: gcj02[1]
};
});
}
function updateRideHailingTaxiPoint(data) {
data.forEach(item => {
pointsRideHailingTaxi.push(
{
lnglat: [item.lon, item.lat],
pointType: 'rideHailing',
...item
}
);
})
rideHailingTaxiCount.value = pointsRideHailingTaxi.length; // 更新count以反映最新的点数量
addCluster('rideHailing');
}
function clearRideHailingPoint() {
pointsRideHailingTaxi = [];
if (clusterRideHailingTaxi) {
clusterRideHailingTaxi.setMap(null); // 清除聚合点
}
return;
}
function addCluster(type) {
if (type === 'cruiseTaxi') {
if (clusterCruiseTaxi) {
clusterCruiseTaxi.setMap(null);
}
clusterCruiseTaxi = new AMap.MarkerCluster(map, pointsCruiseTaxi, {
gridSize: gridSize, // 设置网格像素大小
renderClusterMarker: _renderClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
} else if (type === 'rideHailing') {
if (clusterRideHailingTaxi) {
clusterRideHailingTaxi.setMap(null);
}
clusterRideHailingTaxi = new AMap.MarkerCluster(map, pointsRideHailingTaxi, {
gridSize: gridSize, // 设置网格像素大小
renderClusterMarker: _renderClusterMarker, // 自定义聚合点样式
renderMarker: _renderMarker, // 自定义非聚合点样式
});
}
}
// 聚合的点位样式
const _renderClusterMarker = function (context) {
// console.log(context)
let count = 0;
if (context.clusterData[0].pointType === 'cruiseTaxi') {
count = cruiseTaxiCount.value;
} else if (context.clusterData[0].pointType === 'rideHailing') {
count = rideHailingTaxiCount.value;
}
var factor = Math.pow(context.count / count, 1 / 18);
var div = document.createElement('div');
var Hue = 180 - factor * 180;
var bgColor = 'hsla(' + Hue + ',100%,40%,0.7)';
var fontColor = 'hsla(' + Hue + ',100%,90%,1)';
var borderColor = 'hsla(' + Hue + ',100%,40%,1)';
var shadowColor = 'hsla(' + Hue + ',100%,90%,1)';
div.style.backgroundColor = bgColor;
var size = Math.round(30 + Math.pow(context.count / count, 1 / 5) * 20);
div.style.width = div.style.height = size + 'px';
div.style.border = 'solid 1px ' + borderColor;
div.style.borderRadius = size / 2 + 'px';
div.style.boxShadow = '0 0 5px ' + shadowColor;
div.innerHTML = context.count;
div.style.lineHeight = size + 'px';
div.style.color = fontColor;
div.style.fontSize = '14px';
div.style.textAlign = 'center';
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2));
context.marker.setContent(div)
};
// 不聚合的点位样式
const _renderMarker = function (context) {
// console.log(context)
let content = '<div style="background-color: hsla(180, 100%, 50%, 0.3);' +
'height: 18px; width: 18px; ' +
'border: 1px solid hsl(180, 100%, 40%);' +
' border-radius: 12px;' +
' box-shadow: hsl(180, 100%, 50%) 0px 0px 3px;">' +
'</div>';
if (context.data[0].pointType === 'cruiseTaxi') {
content = '<div style="background-image: url(\'' + cruiseTaxiImg + '\');' +
'height: 45.9px; ' +
'width: 33px;' + // 根据实际图标尺寸调整大小
' background-size: cover;' + // 确保图像覆盖整个区域
' border-radius: 16px;' + // 可以根据需要调整圆角
' box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5);' + // 调整阴影为适当的样式
' display: flex; align-items: center; justify-content: center;">' + // 居中图标
'</div>';
} else if (context.data[0].pointType === 'rideHailing') {
content = '<div style="background-image: url(\'' + rideHailingTaxiImg + '\');' +
'height: 45.9px; ' +
'width: 33px;' + // 根据实际图标尺寸调整大小
' background-size: cover;' + // 确保图像覆盖整个区域
' border-radius: 16px;' + // 可以根据需要调整圆角
' box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.5);' + // 调整阴影为适当的样式
' display: flex; align-items: center; justify-content: center;">' + // 居中图标
'</div>';
}
context.marker.on('click', () => {
showInfoWindow(context.data[0])
})
let offset = new AMap.Pixel(-16, 0);
context.marker.setContent(content)
context.marker.setOffset(offset)
}
const showInfoWindow = async (item) => {
// 根据 item.type 动态选择组件
let ComponentToMount;
if (item.pointType === 'cruiseTaxi') {
ComponentToMount = MapTaxiTooltip;
} else if (item.pointType === 'rideHailing') {
ComponentToMount = MapTaxiTooltip;
}
// 创建一个挂载点
const mountPoint = document.createElement('div');
// 挂载Vue组件
const app = createApp(ComponentToMount, {
item: item,
}
);
app.mount(mountPoint);
// 创建信息窗体并设置内容
infoWindow = new AMap.InfoWindow({
isCustom: true,
content: mountPoint,
offset: new AMap.Pixel(0, -0)
});
// 在地图上打开信息窗体
infoWindow.open(map, [item.lon, item.lat]);
const closeButton = document.querySelector('.close-button');
if (closeButton) {
closeButton.addEventListener('click', () => {
infoWindow.close();
});
}
// 在不需要时卸载组件
infoWindow.on('close', () => {
app.unmount();
});
}
创建线条,定义不同类型样式,线条点击事件
// 线路数据
let lineMapArr = []
let lineJsonData = []
let allRouteInfoList = ref([])
let currentSelectPolyline = null; // 存储当前点击的线路
function updateLine(jsonData) {
const path = [];
lineMapArr = [];
jsonData.forEach((item) => {
// 判断当前线路的类型是否在选中的类型数组中
if (selectedBusLine.value.includes(item.routeCityType)) {
const lineAllPath = [];
const lineArr = item.linePoints.split(',');
lineArr.forEach((i) => {
const linePath = [];
const gcj02 = wgs84togcj02(i.split(' ')[0], i.split(' ')[1]);
linePath.push(gcj02[0], gcj02[1]);
lineAllPath.push(linePath);
});
path.push(lineAllPath);
let color = '';
if (item.routeCityType == 1) {
color = '#2ebd05';
} else if (item.routeCityType == 2) {
color = '#1784fc';
} else if (item.routeCityType == 3) {
color = '#FCC881';
}
var polyline = new AMap.Polyline({
path: lineAllPath,
isOutline: false,
// outlineColor: '#ffffff',
borderWeight: 3,
strokeColor: color,
strokeOpacity: 1,
strokeWeight: 4,
// 折线样式还支持 'dashed'
strokeStyle: 'solid',
// strokeStyle是dashed时有效
strokeDasharray: [15, 5],
lineJoin: 'round',
lineCap: 'round',
zIndex: 50,
routeCode: item.routeCode
});
polyline.setMap(map);
// 为线图层添加点击事件
polyline.on('click', (e) => {
// 如果有之前点击的线路,将其颜色恢复
if (currentSelectPolyline) {
currentSelectPolyline.setOptions({
strokeColor: currentSelectPolyline.getExtData().originalColor
});
}
// 将当前点击的线路设置为红色
polyline.setOptions({
strokeColor: '#FEA373'
});
// 保存当前点击的线路
currentSelectPolyline = polyline;
// 保存原始颜色到扩展数据
polyline.setExtData({
originalColor: color
});
// 显示信息窗口
showBusInfoWindow({
routeCode: item.routeCode,
pointType: 'line',
lon: e.lnglat.getLng(),
lat: e.lnglat.getLat()
});
});
lineMapArr.push(polyline);
}
});
map.add(lineMapArr);
}
适应分辨率
如果使用了分辨率适应框架,可以让地图监听也做适应,否则点击事件会出问题,比如创建线条的点击事件时;
const initMap = () => {
AMapLoader.load({
key: "xxx", // 申请好的Web端开发者Key,首次调用 load 时必填
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.MoveAnimation', 'AMap.MarkerCluster'] // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap) => {
map = new AMap.Map('container1', {
//设置地图容器id
mapStyle: 'amap://styles/ca482dc75f8d435993e158d63b3f8695',
viewMode: '2D', //是否为3D地图模式
zoom: 14, //初始化地图级别
// center: [120.576963, 30.048733] //初始化地图中心点位置
center: [120.876508, 30.044303]
});
map.setDefaultCursor('pointer')
publicBicycleStationIcon = new AMap.Icon({
// 图标尺寸
size: new AMap.Size(33, 49.9),
// 图标的取图地址
image: stationPointImg,
// 图标所用图片大小
imageSize: new AMap.Size(33, 49.9),
// 图标取图偏移量
imageOffset: new AMap.Pixel(0, 0)
});
window.addEventListener('resize', resetMapSize);
resetMapSize(); // 初始调用,确保地图容器在首次渲染时也正确
})
};
// 定义 fnResize 方法,计算当前缩放比例
const fnResize = () => {
let screenWidth = document.body.clientWidth || document.documentElement.clientWidth;
let screenHeight = document.body.clientHeight || document.documentElement.clientHeight;
let defWidth = 1920;
let defHeight = 1080;
let scaleX = screenWidth / defWidth;
let scaleY = screenHeight / defHeight;
return {scaleX: scaleX, scaleY: scaleY};
};
// 定义 resetMapSize 方法
const resetMapSize = () => {
const scaleWH = fnResize();
console.log(scaleWH)
const mapContent = document.getElementById("container1");
if (mapContent) {
mapContent.style.transform = `scale(${1 / scaleWH.scaleX}, ${1 / scaleWH.scaleY})`;
}
};