uniapp | 安卓端集成高德地图的智能多边形绘制功能实现
·
前言
在uniapp中实现的智能多边形绘制页面,涵盖高德地图API集成、逻辑层与RenderJS通信机制,以及移动端地图交互的完整实现方案。
核心技术栈
| 模块 | 技术 | 作用 |
|---|---|---|
| 跨平台框架 | uniapp | 一套代码多端运行(H5/小程序/APP) |
| 地图引擎 | 高德地图JSAPI | 提供地图渲染、定位、标记等核心能力 |
| 通信机制 | RenderJS | 解耦逻辑层与地图渲染层,避免DOM操作污染 |
逻辑架构流程图

功能实现全流程解析
页面初始化流程
// 逻辑层 - 页面加载
onLoad(option) {
this.originPolygon = option.polygon || ""; // 接收传入的多边形数据
this.checkLoginStatus(); // 检查登录状态
}
// RenderJS - 初始化地图
mounted() {
this.initAMap(); // 加载高德地图
}
关键设计:通过option.polygon接收上一个页面传入的多边形数据,实现"编辑模式"。
高德地图核心初始化
initAMap() {
window._AMapSecurityConfig = { securityJsCode: '你的高德地图密钥' };
AMapLoader.load({
key: "你的高德地图的key",
version: "1.4.15",
plugins: ["AMap.PlaceSearch", "AMap.Scale", "AMap.ToolBar", "AMap.Geolocation", 'misc/PositionPicker'],
AMapUI: { version: '1.0', plugins: ['misc/PathSimplifier'] }
}).then(AMap => {
// 创建地图实例
map = new AMap.Map("container", {
pitch: 50,
viewMode: '3D',
zoom: 16,
center: [84.920629, 45.417129],
layers: [new AMap.TileLayer.Satellite(), new AMap.TileLayer.RoadNet()]
});
// 添加地图控件
map.addControl(new AMap.Scale());
map.addControl(new AMap.ToolBar());
map.addControl(geolocation); // 添加定位控件
});
}
技术亮点:
- 使用
AMapLoader安全加载地图API - 配置3D视角+卫星图层提升地图体验
- 添加缩放控件和工具栏增强交互性
智能多边形绘制逻辑
addMarker(position) {
// 保存坐标
polygonSaveTemp.push([position.lng, position.lat]);
// 创建标记点
const marker = new AMap.Marker({ position });
map.add(marker);
markers.push(marker);
}
createPolygon() {
if (markers.length >= 3) {
const path = markers.map(marker => marker.getPosition());
// 移除旧多边形
if (polygon) map.remove(polygon);
// 创建新多边形
polygon = new AMap.Polygon({
path,
strokeColor: "#FF33FF",
strokeOpacity: 0.2,
strokeWeight: 3,
fillColor: "#1791fc",
fillOpacity: 0.35
});
map.add(polygon);
}
}
交互设计:
- 用户点击地图添加标记点(至少3个点)
- 点击"保存"按钮自动创建多边形
- 保留多边形样式(蓝色填充+紫色边框)
逻辑层与RenderJS通信机制
// RenderJS中保存多边形
saveDiv.addEventListener('click', () => {
this.createPolygon();
let result = JSON.stringify(polygonSaveTemp);
this.$ownerInstance.callMethod('polygonSave', result);
});
// 逻辑层接收结果
polygonSave(result) {
console.log("逻辑层多边形数据-----》", result);
this.polygon = result;
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit("polygonResult", { result: JSON.stringify(this.polygon) });
uni.navigateTo({ url: "/pages/main/main" });
}
通信优势:
- 通过
callMethod实现双向通信 - 避免在逻辑层直接操作DOM
- 保持代码职责清晰(逻辑层处理数据,RenderJS处理渲染)
完整代码:
<template>
<view>
<view id="container">
<view
:originPolygon="originPolygon"
:change:originPolygon="renderJS.receiveOriginPolygon"
/>
</view>
</view>
</template>
<script>
import { useUserStore } from "../../stores/user";
export default {
data() {
return {
maph: 0,
//如果需要编辑多边形,传进来多边形字符串
originPolygon: "", // 接收传入的多边形数据
polygon: [], // 存储绘制的多边形坐标
currentLocation: null, // 当前位置信息
};
},
onLoad(option) {
// 页面加载时检查用户登录状态
this.checkLoginStatus();
// 页面加载时获取传入的多边形参数
this.originPolygon = option.polygon || "";
},
mounted() {
//获取当前定位
this.$nextTick(() => {
this.getCurrentLocation();
});
},
methods: {
// 检查用户登录状态
checkLoginStatus() {
try {
// 使用 Pinia store 检查登录状态
const userStore = useUserStore();
// 检查是否已登录
if (!userStore.isLogin) {
uni.showToast({
title: "请先登录",
icon: "none",
duration: 2000,
});
// 延迟跳转以显示提示信息
setTimeout(() => {
uni.redirectTo({
url: "/pages/login/login",
});
}, 2000);
return false;
}
return true;
} catch (error) {
console.error("检查登录状态失败:", error);
uni.redirectTo({
url: "/pages/login/login",
});
return false;
}
},
getCurrentLocation() {
uni.getLocation({
type: "gcj02",
success: (res) => {
this.currentLocation = {
lat: res.latitude,
lng: res.longitude,
};
// this.renderJS.receiveCurrentLocation(this.currentLocation)
// this.$callMethod("receiveCurrentLocation",this.currentLocation)
// console.log("this.$ownerInstance.callMethod--->",this.$ownerInstance.callMethod)
// console.log("this.$callMethod--->",this.$callMethod)
// 延迟调用,确保 renderJS 就绪
// setTimeout(() => {
// if (this.$ownerInstance && this.$ownerInstance.callMethod) {
// this.$callMethod("receiveCurrentLocation", this.currentLocation);
// } else {
// console.warn("renderJS 未就绪,跳过调用");
// }
// }, 500);
},
fail: (err) => {
console.log("getCurrentLocation--fail--->", err);
},
});
},
polygonSave(result) {
// 保存多边形数据并返回给上一个页面
console.log("逻辑层多边形数据-----》", result);
this.polygon = result;
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit("polygonResult", {
result: JSON.stringify(this.polygon),
});
uni.navigateTo({ url: "/pages/main/main" });
},
},
};
</script>
<script module="renderJS" lang="renderjs">
import AMapLoader from "@amap/amap-jsapi-loader";
let map = null;// 地图实例
let positionPicker = null;// 位置选择器
let polygon = null;// 多边形对象
let markers = [];// 标记点数组
let positionAddress = null;// 当前位置信息
let polygonSaveTemp = [];// 临时存储多边形坐标
let geolocation = null;// 地理定位对象
export default {
name: "map-view",
mounted() {
// console.log("renderJS开始挂载---》",this)
this.initAMap();
},
methods: {
initAMap() {
// 设置安全密钥
window._AMapSecurityConfig = {
securityJsCode: 'your keycode',
}
// 加载高德地图API
AMapLoader.load({
key: "your key",
version: "1.4.15",
plugins: ["AMap.PlaceSearch", "AMap.Scale", "AMap.ToolBar","AMap.Geolocation", 'misc/PositionPicker'],
AMapUI: {
version: '1.0',
plugins: ['misc/PathSimplifier', 'overlay/SimpleMarker']
}
}).then((AMap) => {
// 创建地图实例
map = new AMap.Map("container", {
pitch: 50, //地图俯仰角度
viewMode: '3D',
zoom: 16,
center: [84.920629, 45.417129], // 设置初始中心点 [lng, lat]
layers: [new AMap.TileLayer.Satellite(), new AMap.TileLayer.RoadNet()],
visible: true,
resizeEnable: true,
zooms: [2, 20], //地图显示的缩放级别范围
});
// 初始化地理定位
geolocation = new AMap.Geolocation({
enableHighAccuracy: true, //是否使用高精度定位,默认:true
timeout: 10000, //超过10秒后停止定位,默认:5s
buttonPosition: 'RB', //定位按钮的停靠位置
buttonOffset: new AMap.Pixel(10, 20), //定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
zoomToAccuracy: true, //定位成功后是否自动调整地图视野到定位点
});
scale=new AMap.Scale({
visible: true,
})
toolBar=new AMap.ToolBar({
visible: true,
position:'RT'
})
map.addControl(scale);
map.addControl(toolBar);
map.addControl(geolocation);
// geolocation.getCurrentPosition((status, result) => {
// console.log('geolocation.getCurrentPosition----->',status, result)
// })
// 加载位置选择器
AMapUI.loadUI(['misc/PositionPicker'], (PositionPickerClass) => {
positionPicker = new PositionPickerClass({
mode: 'dragMap',
map: map,
});
// 监听位置选择成功事件
positionPicker.on('success', (positionResult) => {
console.log('positionPicker.on--->', positionResult.position)
// 处理成功获取位置的结果,保存当前位置
positionAddress = positionResult.position
});
positionPicker.on('fail', (positionResult) => {
console.log(positionResult)
// 处理失败获取位置的结果
});
map.on("click", (e) => {
console.log("map.on---->",e.lnglat)
// 添加标记点
this.addMarker(e.lnglat);
});
// 启动位置选择器
// positionPicker.start()
});
// 地图加载完成事件
map.on("complete", (e) => {
this.initDivs()// 初始化控制按钮
});
}).catch((e) => {
console.error(e);
});
},
//接收逻辑层传来的当前位置
// receiveCurrentLocation(newPosition){
// console.log("receiveCurrentLocation----->",map,newPosition);
// map.setCenter([newPosition.lng, newPosition.lat]);
// map.setZoom(18);
// },
addMarker(position) {
// 检查位置信息是否存在
if (!position) {
console.warn('位置信息为空,无法添加标记');
uni.showToast({
title: '请先进行定位',
icon: 'none'
});
return;
}
console.log('position----->', position)
// 保存坐标到临时数组
let temp = [position.lng, position.lat]
polygonSaveTemp.push(temp)
// 创建并添加标记点
const marker = new AMap.Marker({
position: position,
});
map.add(marker);
markers.push(marker);
},
createPolygon() {
if (markers.length >= 3) {
const path = markers.map(marker => marker.getPosition());// 获取所有标记点坐标
// 如果已有旧多边形,则先移除
if (polygon) {
map.remove(polygon);
}
// 创建新多边形
polygon = new AMap.Polygon({
path: path,
strokeColor: "#FF33FF", //线颜色
strokeOpacity: 0.2, //线透明度
strokeWeight: 3, //线宽
fillColor: "#1791fc", //填充色
fillOpacity: 0.35, //填充透明度
});
map.add(polygon);// 添加到地图
}
},
initDivs() {
// 创建底部控制栏容器
const div = document.createElement('div');
div.style.display = 'flex';
div.style.flexDirection = 'row';
div.style.width = 'calc(100% - 40px)';
div.style.alignItems = 'center';
div.style.height = '40px';
div.style.justifyContent = 'space-between';
div.style.position = 'fixed';
div.style.bottom = '40px';
div.style.left = '20px';
div.style.fontSize = '30px';
div.style.zIndex = 999
document.body.appendChild(div);
const clearDiv = this.createDiv('清除')
div.appendChild(clearDiv)
clearDiv.addEventListener('click', () => {
// 清除所有标记
if (markers && markers.length > 0) {
// 逐个移除标记
markers.forEach(marker => {
if (marker && map) {
map.remove(marker);
}
});
markers = [];
}
// 移除多边形
if (polygon && map) {
map.remove(polygon);
polygon = null;
}
// 清空保存的坐标数据
polygonSaveTemp = [];
// 清除地图上的所有覆盖物(可选)
if (map) {
map.clearMap();
}
});
const saveDiv = this.createDiv('保存')
div.appendChild(saveDiv)
saveDiv.addEventListener('click', () => {
//创建多边形
this.createPolygon();
let result = JSON.stringify(polygonSaveTemp)
this.$ownerInstance.callMethod('polygonSave', result)
console.log('保存')
});
// const confirmDiv = this.createDiv('定位')
// div.appendChild(confirmDiv)
// confirmDiv.addEventListener('click', () => {
// geolocation.getCurrentPosition()
// });
},
//创建单个div
createDiv(inner) {
let div = document.createElement('div');
div.style.width = '70px';
div.style.height = '30px';
div.style.background = '#FFFFFF';
div.style.boxShadow = '0px 0px 3px 1px rgba(0, 0, 0, 0.1)';
div.style.borderRadius = '10px';
div.style.fontSize = '15px';
div.style.lineHeight = '30px';
div.style.textAlign = 'center';
div.innerHTML = inner
return div
},
receiveOriginPolygon() {
console.log(polygonSaveTemp)
// 处理接收到的多边形数据
// 可以将 polygonData 解析并用于初始化地图上的多边形
},
},
};
</script>
<style scoped>
#container {
width: 100vw;
height: 90vh;
}
</style>
总结:
- 优先使用RenderJS:避免在逻辑层操作DOM,这是uniapp地图开发的核心原则
- 坐标系转换要谨慎:高德API使用
[lng, lat],业务系统常用[lat, lng] - 通信机制要明确:
callMethod+eventChannel是跨层通信的黄金组合 - 用户体验优先:固定底部操作按钮、3D地图视角、即时反馈都是提升体验的关键
更多推荐
所有评论(0)