前情提要:uniapp自带的 map 标签实在是不好用(也可能是笔者不懂…-_-||),因此最终选择的方案,还是H5方式的高德地图。但是呢,在app端又识别不了跟dom相关的对象的,后根据文档说明,使用renderjs解决H5渲染地图的问题。
<!--
* @Description: js方式的高德基础地图底图
* @Author:
* @Date:
* @LastEditors:
* @LastEditTime:
-->
<template>
<view
id="mapContainer"
class="relative w-full h-full"
:init-map="initMap"
:change:init-map="AMapInstance.initAmap"
:markers-prop="markers"
:change:markers-prop="AMapInstance.drawPoint"
:center="center"
:change:center="AMapInstance.setCenter"
:location-prop="location"
:change:location-prop="AMapInstance.setLocation"
:zoom-prop="zoom"
:change:zoom-prop="AMapInstance.setZoom"
/>
</template>
<script>
export default {
props: {
markers: {
type: Object,
default: () => ({}),
},
center: { // 中心点坐标
type: Array,
default: () => [],
},
location: { // 定位坐标
type: Array,
default: () => [],
},
mapCover: {
type: Array,
default: () => [],
},
zoom: {
type: Number,
default: 10,
},
},
data() {
return {
initMap: true,
direction: 0,
};
},
watch: { },
mounted() {
// 罗盘
uni.onCompassChange(function (res) {
// console.log(`罗盘${res.direction}`);
this.direction = res.direction;
});
},
methods: {
mapLoaded() {
this.$emit('mapLoaded', true);
},
showMarkerDetail(item) {
this.$emit('clickMarkDetail', item);
},
},
};
</script>
<script module="AMapInstance" lang="renderjs">
// renderjs运行在视图层的js,只支持app-vue和h5(简单来说就是开了另外一条线程)
// 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力(减少通讯损耗提升性能,例如一些手势或canvas动画的场景
export default {
data() {
return {
amap: null,
infoWindow: null,
overlayGroups: null, // 覆盖物群组
markersAll: [], // 点集合组
cacheMarkers: {},
projectLineData: [],
longitude: 104.07275,
latitude: 30.57899,
ImgDiseaseBlue0: 'static/images/map/disease-blue-0.png',
ImgDiseaseBlue1: 'static/images/map/disease-blue-1.png',
ImgDiseaseCyan0: 'static/images/map/disease-cyan-0.png',
ImgDiseaseCyan1: 'static/images/map/disease-cyan-1.png',
ImgDiseaseOrange0: 'static/images/map/disease-orange-0.png',
ImgDiseaseOrange1: 'static/images/map/disease-orange-1.png',
ImgLocation: 'static/images/map/location.png',
};
},
mounted() {
if (window && window.AMap) {
this.initAmap();
} else {
window._AMapSecurityConfig = {
securityJsCode: '***',
};
const script = document.createElement('script');
script.src = 'https://webapi.amap.com/maps?v=1.4.8&key=***&plugin=AMap.ControlBar';
script.onload = () => {
this.initAmap()
};
document.head.appendChild(script);
}
},
methods: {
initAmap(newValue, oldValue, ownerVm, vm) {
this.amap = new AMap.Map('mapContainer', {
zoom: 10, // 显示的缩放级别
// zooms: [2, 30], //地图显示的缩放级别范围, 默认为 [2, 20] ,取值范围 [2 ~ 30]
center: [this.longitude, this.latitude], // todo 中心点坐标
mapStyle: 'amap://styles/57994c871bb604a4c79184f5f65d8782', // todo 地图样式
resizeEnable: true, // 是否监控地图容器尺寸变化
});
ownerVm.callMethod('mapLoaded', true)
// 获取点击点
var markers = [];
this.amap.on('click', (e) => {
this.amap.remove(markers);
this.pointerMaker = new AMap.Marker({
icon: "https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png",
position: new AMap.LngLat(e.lnglat.getLng(), e.lnglat.getLat()),
//anchor:'bottom-center',
offset: new AMap.Pixel(-9, -31),
});
this.amap.add(this.pointerMaker);
this.pointerLonLat = [e.lnglat.getLng(), e.lnglat.getLat()];
// 真机中,在renderjs中识别不了常规的this对象,因此相关的api不可用了,只能兼容着写了
// #ifdef H5
this.$uniStorage.set('A_MAP_POINTER_LONLAT', { lonLat: this.pointerLonLat });
//#endif
// #ifdef APP-PLUS || APP-PLUS-NVUE
ownerVm.callMethod('setPinter', { lonLat: this.pointerLonLat })
//#endif
markers.push(this.pointerMaker);
});
// 动态设置css,去掉高德的logo(奇怪的是,其他组件或页面不需要加这个,只有首页需要...)PS:按需,是否加以下代码
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '.amap-logo {opacity: 0 !important; display: none !important;z-index: -99999999 !important;}';
document.head.appendChild(style);
},
setCenter(newValue, oldValue, ownerVm, vm) {
if (!this.amap) return;
if(!newValue || newValue.length === 0) return;
const position = new AMap.LngLat(newValue[0], newValue[1]); // 标准写法
// 简写 var position = [116, 39];
this.amap&&this.amap.setCenter(position);
},
// 定位
setLocation(newValue, oldValue, ownerVm, vm) {
if(!newValue || newValue.length === 0) return;
const position = new AMap.LngLat(newValue[0], newValue[1]); // 标准写法
// 简写 var position = [116, 39];
this.amap.setCenter(position);
const wh = this.getWH(68, 56, 32);
const mark = new AMap.Marker({
position: position,
icon: new AMap.Icon({ image: this.ImgLocation, size: new AMap.Size(wh[0], wh[1]), imageSize: new AMap.Size(wh[0], wh[1]) }),
offset: new AMap.Pixel(-wh[0] / 2, -wh[1]),
autoRotation: true,
angle: 180
});
this.amap.add(mark)
},
// 设置缩放等级
setZoom(level) {
if (!this.amap) return;
this.amap.setZoom(level || 12);
},
// 清除所有marker点位
clearMarkerPoints() {
if (!this.amap) return;
this.markersAll = [];
if (this.overlayGroups) {
this.amap.remove(this.overlayGroups);
this.overlayGroups = null;
}
},
// 画圆形
drawCircle(coors){
if (!this.amap) return;
this.clearCircle()
const circle = new AMap.Circle({
center: coors || [116.433322, 39.900255],
radius: 1000, // 半径
borderWeight: 3,
strokeColor: '#FF33FF',
strokeWeight: 6,
strokeOpacity: 0.2,
fillOpacity: 0.4,
strokeStyle: 'dashed',
strokeDasharray: [10, 10],
// 线样式还支持 'dashed'
fillColor: '#1791fc',
zIndex: 50
});
this.circleGroups = circle;
this.amap.add(circle);
},
// 画点
drawPoint(newValue, oldValue, ownerVm, vm) {
this.clearMarkerPoints()
if (!newValue || !newValue.data) return;
if (!this.amap) return;
this.cacheMarkers = newValue;
const data = newValue.data;
const type = newValue.type;
if (type === '0') {
if (!data.length) return;
const markers = data.map((item) => {
let iconImgD = this.ImgDiseaseOrange1;
let iconImgA = this.ImgDiseaseOrange0;
if (item.type === 1) {
iconImgD = this.ImgDiseaseBlue1;
iconImgA = this.ImgDiseaseBlue0;
} else if (item.type === 2) {
iconImgD = this.ImgDiseaseCyan1;
iconImgA = this.ImgDiseaseCyan0;
}
const wh = this.getWH(64, 90, 30);
const marker = new AMap.Marker({
position: new AMap.LngLat(item.position[0], item.position[1]),
icon: new AMap.Icon({ image: iconImgD, size: new AMap.Size(wh[0], wh[1]), imageSize: new AMap.Size(wh[0], wh[1]) }),
extData: item,
offset: new AMap.Pixel(-wh[0] / 2, -wh[1]),
});
marker.on('click', (e) => {
for (let i = 0; i < markers.length; i++) {
const it = markers[i].getExtData();
let iconImgD = this.ImgDiseaseOrange1;
if (it.type === 1) {
iconImgD = this.ImgDiseaseBlue1;
} else if (it.type === 2) {
iconImgD = this.ImgDiseaseCyan1;
}
const preIcon = new AMap.Icon({
image: iconImgD,
size: new AMap.Size(wh[0], wh[1]), // 图标大小
imageSize: new AMap.Size(wh[0], wh[1]),
});
markers[i].setIcon(preIcon);
markers[i].setOffset(new AMap.Pixel(-wh[0] / 2, -wh[1]));
}
const whn = this.getWH(96, 160, 36);
// eslint-disable-next-line no-undef
const clickIcon = new AMap.Icon({
image: iconImgA,
size: new AMap.Size(whn[0], whn[1]), // 图标大小
imageSize: new AMap.Size(whn[0], whn[1]),
});
e.target.setIcon(clickIcon);
e.target.setOffset(new AMap.Pixel(-whn[0] / 2, -whn[1]));
ownerVm.callMethod('showMarkerDetail', item);
});
return marker;
});
if (this.markersAll.length > 0) {
this.markersAll = [...this.markersAll, ...markers];
} else {
this.markersAll = Object.assign(this.markersAll, markers);
}
// eslint-disable-next-line no-undef
this.overlayGroups = new AMap.OverlayGroup(this.markersAll);
this.amap.add(this.overlayGroups);
}
},
// 画线
drawLine(data = []) {
if (!data.length) return;
if (!this.amap) return;
const opts = {
strokeWeight: 10,
strokeStyle: 'solid',
zIndex: 10,
isOutline: true,
outlineColor: '#ffeeff',
borderWeight: 3,
strokeColor: "#3366FF",
strokeOpacity: 1,
// 折线样式还支持 'dashed',strokeStyle是dashed时有效
strokeDasharray: [10, 5],
lineJoin: 'round',
lineCap: 'round',
zIndex: 50,
bubble: true, // 将覆盖物的鼠标或touch等事件冒泡到地图上
};
if (type === 'loopLine') {
data.forEach((item) => {
opts.path = item.position;
const polyline = new AMap.Polyline(opts);
this.amap.add(polyline);
this.projectLineData.push(polyline);
polyline.on('mouseover', async (e) => {
console.log('drawLineCustom-polyline-item>>', item);
});
});
this.amap.setFitView(this.projectLineData);
}
},
// 根据需要,通过原始宽高和结果宽度,获取结果图片的大小
getWH(ow, oh, rw) {
return [rw, rw * oh / ow];
},
},
};
</script>
<style scoped>
::v-deep .amap-logo {
opacity: 0 !important;
display: none !important;
}
::v-deep .amap-copyright {
opacity: 0 !important;
display: none !important;
}
</style>
特别说明:在 renderjs 中,是不能引入请求api的(真机报错)等普通依赖js方法的,所有跟渲染图层相关的操作,尽量都通过 service 层传参到 renderjs 中处理!