uniapp 自带的map标签有时候不满足项目需求,就要在app中加载高德地图来实现更多的地图操作,而在uniapp中不能操作dom,所以只能用uniapp提供的render.js来实现加载高德地图了。
什么是renderjs?
renderjs是一个运行在视图层的js。他比wsx更加强大。它只支持app-vue和web.
renderjs的主要作用有2个:
- 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
- 在视图层操作dom,运行 for web 的 js库
功能详解
- 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
uni-app的app端逻辑层和视图层是分离的,这种机制有很多好处,但也有一个副作用是在造成了两层之间通信阻塞。尤其是App的Android端阻塞问题影响了高性能应用的制作。
renderjs运行在视图层,可以直接操作视图层的元素,避免通信折损。
在hello uni-app的canvas示例中,App端使用了renderjs,由运行在视图层的renderjs直接操作视图层的canvas,实现了远超微信小程序的流畅canvas动画示例。具体在hello uni-app示例中体验,对比App端和小程序端的性能差异。
- 在视图层操作dom,运行for web的js库
官方不建议在uni-app里操作dom,但如果你不开发小程序,想使用一些操作了dom、window的库,其实可以使用renderjs来解决。
在app-vue环境下,视图层由webview渲染,而renderjs运行在视图层,自然可以操作dom和window。
这是一个基于renderjs运行echart完整版的示例:renderjs版echart
同理,f2、threejs等库都可以这么用。
renderjs说完了说说高德地图的功能实现
实现效果:区域内打点,点击点位弹窗显示详细信息。
<template>
<view>
<view id="amap" :style="mapStyle" class="amap-style" :markers="markers" :change:markers="amap.updateMarker"></view>
</view>
</template>
<!-- 高德地图 renderjs视图层 -->
<script module="amap" lang="renderjs">
import { geojson } from '@/utils/geojson.js';
let map = undefined,
cluster = undefined;
export default {
data() {
return {
isMapLoadSuccess: false
}
},
mounted() {
console.log('renderjs mounted');
// 清除地图数据
this.reset();
// 注意:异步回调函数的声明应该在 JSAPI 引入之前,函数名与callback=onLoad中对应
if (!window.isOnRady) {
window.isOnRady = () => {
this.init();
};
} else {
this.init();
}
// 异步加载高德地图 JSAPI
if (!window.AMap) {
this.loadAMap();
}
},
methods: {
// 异步加载高德地图 JSAPI
loadAMap() {
// 动态异步引入较大类库避免影响页面展示
const myKey = '你的key';
const myCode = "你的秘钥";
const url = `https://webapi.amap.com/maps?v=2.0&key=${myKey}&callback=isOnRady`;
const jsapi = document.createElement('script');
jsapi.charset = 'utf-8';
jsapi.src = url;
document.head.appendChild(jsapi);
// 安全密钥
window._AMapSecurityConfig = {
securityJsCode: myCode,
};
console.log('加载高德地图插件');
},
// 初始化
init() {
map = new AMap.Map('amap', this.mapDefaultOptions);
map.on('complete', () => {
console.log('地图加载完成');
this.isMapLoadSuccess = true;
this.initMarkers();
this.drawBounds()
})
},
// 地图相关数据初始化
reset() {
map && (map = undefined);
// 如果有之前的数据,先清除
if (cluster) {
console.log('清除点聚合');
cluster.setMap(null);
cluster = undefined;
}
},
// 生成点聚合点位
initMarkers() {
// 添加判断,防止地图没有加载完成就渲染
if (!(this.isMapLoadSuccess && this.markers.length)) {
return
}
// 根据官方示例数据形式
const point = this.markers;
// 先调整地图中心点
const center = point[0].lnglat || this.mapDefaultOptions.center;
map.setCenter(center);
// 加载点聚合插件
map.plugin(["AMap.MarkerCluster"], () => {
cluster = new AMap.MarkerCluster(map, point, {
gridSize: 80, // 聚合网格像素大小
renderClusterMarker: (context) => { // 对聚合点位的渲染
// console.log('context:', context); // 入参中有四个数据 count data indexs marker
// 修改聚合点位样式
this.createClusterMarker(context);
// 设置聚合点位点击事件
const marker = context.marker;
marker.on('click', (e) => {
let mapZoom = map.getZoom();
if (mapZoom < 20) {
mapZoom += 2;
}
map.setZoomAndCenter(mapZoom, e.lnglat);
})
},
renderMarker: ({
data,
marker
}) => { // 对非聚合点位的渲染
// console.log(context); // 入参中有四个数据 count data indexs marker
// 获取自己设置的name
if(data[0].name) {
// console.log(data[0].name);
}
marker.setIcon(this.creatAMapIcon());
marker.setAnchor('bottom-center');
// 设置非聚合点位点击事件
marker.on('click', (e) => {
map.setCenter(e.lnglat);
this.tapMarker(data);
})
}
});
});
},
// 更新数据
updateMarker(newVal) {
console.log('renderjs 更新:', newVal.length);
this.initMarkers();
},
// 生成高德地图Icon
creatAMapIcon(type) {
const iconOptions = {
image: 'http://safety-1257229566.cos.ap-beijing.myqcloud.com/2024/08/29/3387843080304d6db8aa8545b50983bf.png',
imageSize: new AMap.Size(45, 45)
}
return new AMap.Icon(iconOptions);
},
// 聚合点位样式(采用高德地图)
createClusterMarker(context) {
const count = this.markers.length + 1000;
const factor = Math.pow(context.count / count, 1 / 18);
const div = document.createElement('div');
const Hue = 180 - factor * 180;
const bgColor = 'red';
const fontColor = 'hsla(' + Hue + ',100%,90%,1)';
const borderColor = '#f2b1b1';
const shadowColor = '#f2b1b1';
div.style.backgroundColor = bgColor;
const size = Math.round(20 + 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);
},
// 非聚合marker点击事件
tapMarker(data) {
console.log('点位信息',data)
this.openInfo(data)
},
// 自定义弹窗
openInfo(data) {
//构建信息窗体中显示的内容
var info = [];
info.push("<div style="background: white;width: 150px;padding: 10px;border-radius: 10px">\n" +
` <div style="font-size: 12px;width: 150px;">\n` +
` <div style="font-weight: 600;color:red;">${data[0].name}</div>\n` +
` <div style="margin: 5px 10px;">负责人: ${data[0].leadingPerson}</div>\n` +
` <div style="margin: 5px 10px;">电话: ${data[0].leadingPhone}</div>\n` +
` <div style="margin: 5px 10px;">归属地: ${data[0].belongTo}</div>\n` +
" </div>\n" +
" </div>");
const infoWindow = new AMap.InfoWindow({
isCustom: false,// 是否自定义窗体
anchor: false,// 信息锚点
autoMove:false,// 是否自动调整窗体到视野内
content: info.join(""),// 显示内容,可以是HTML要素字符串或者HTMLElement对象
offset: new AMap.Pixel(-10, -70),// 点位偏移
});
let loglat = data[0].geo.split(",");
let position = new AMap.LngLat(loglat[0], loglat[1]);// 经纬度坐标,用来描述地图上的一个点位置
infoWindow.open(map ,position);
},
// 根据边界数据绘制区域区域边界数据
drawBounds(){
let coordinates = geojson.features[0].geometry.coordinates;
let boundsMap = coordinates[0].map((item) => {
return new AMap.LngLat(item[0], item[1]);
});
let bounds = [boundsMap];
if (bounds) {
for (var i = 0; i < bounds.length; i += 1) {
//构造MultiPolygon的path
bounds[i] = [bounds[i]];
}
let polygon = new AMap.Polygon({
strokeWeight: 1,
path: bounds,
fillOpacity: 0.4,
fillColor: 'rgba(200, 245, 239, 1)',
strokeColor: '#3BC4B1'
});
map.add(polygon);
map.setFitView(polygon); //视口自适应
}
}
},
}
</script>
<script>
export default {
name: 'DetailsMap',
data() {
return {
show: false,
windowHeight: 0,
windowWidth: 0,
markers: [],
form: {},
mapDefaultOptions: {
center: [120.7145, 41.5783],
zoom: 14,
resizeEnable: false //窗口大小调整
}
};
},
computed: {
mapStyle() {
return {
height: this.windowHeight + 'px'
};
}
},
onLoad(option) {
console.log('service onLoad');
this.windowHeight = uni.getSystemInfoSync().windowHeight;
this.windowWidth = uni.getSystemInfoSync().windowWidth;
uni.onWindowResize((res) => {
// 监听屏幕尺寸变化
this.windowHeight = res.size.windowHeight;
this.windowWidth = res.size.windowWidth;
});
this.getMarkList();
},
methods: {
// 模拟接口请求数据
getDataApi() {
setTimeout(() => {
this.markers = [
{
weight: 8,
lnglat: ['108.939621', '34.343147'],
extData: {
// 自定义携带的其他数据
name: '123'
}
},
{
weight: 1,
lnglat: ['113.370643', '22.938827']
},
{
weight: 1,
lnglat: ['112.985037', '23.15046']
},
{
weight: 1,
lnglat: ['110.361899', '20.026695']
},
{
weight: 1,
lnglat: ['121.434529', '31.215641']
}
];
}, 1000);
}
}
};
</script>
<style lang="scss" scoped>
/* 去除高德logo */
::v-deep .amap-logo {
display: none !important;
}
/* 去掉高德的版本号 */
::v-deep .amap-copyright {
opacity: 0 !important;
}
</style>