别再到处找教程了!手把手教你用uni-app的map组件搞定高德地图定位、撒点和画图
别再到处找教程了!手把手教你用uni-app的map组件搞定高德地图定位、撒点和画图
每次开发地图功能时,你是不是总在各大论坛和博客间来回切换,试图拼凑出一个完整的解决方案?作为过来人,我完全理解这种痛苦。本文将带你一次性解决uni-app中高德地图集成的所有核心问题,从基础配置到高级功能实现,让你彻底告别碎片化学习。
1. 高德地图Key申请与基础配置
在开始编码前,我们需要先获取高德地图的开发者Key。这个Key相当于使用高德地图服务的通行证,没有它,所有地图功能都无法正常工作。
获取Key的具体步骤:
- 访问高德开放平台官网并注册开发者账号
- 进入"控制台"→"应用管理"→"创建新应用"
- 为应用添加"Web服务"和"Web端(JS API)"两个平台
- 提交后即可获取到专属的API Key
拿到Key后,我们需要在uni-app项目中配置它。打开manifest.json文件,在"App模块配置"中找到"Map(地图)",勾选"高德地图"并填入你的Key:
"maps": { "amap": { "key": "你的高德地图Key" } }注意:高德地图的Key有调用次数限制,个人开发者每天有3000次免费调用额度,超出后需要付费。开发阶段建议开启"测试模式"以避免不必要的消耗。
2. 基础地图展示与定位功能实现
让我们从最基础的地图展示开始。uni-app内置的map组件已经封装了高德地图的大部分功能,使用起来非常方便。
基础地图组件代码:
<template> <view> <map id="myMap" style="width: 100%; height: 80vh" :latitude="latitude" :longitude="longitude" :show-location="true" ></map> </view> </template> <script> export default { data() { return { latitude: 39.90469, // 默认北京中心坐标 longitude: 116.40717 } }, mounted() { this.initMap() }, methods: { initMap() { this.mapContext = uni.createMapContext('myMap', this) } } } </script>这段代码已经可以展示一个基础地图,但还没有实现真正的定位功能。要实现定位,我们需要调用uni-app的定位API:
methods: { getLocation() { uni.getLocation({ type: 'gcj02', // 必须使用gcj02坐标系 success: (res) => { this.latitude = res.latitude this.longitude = res.longitude this.mapContext.moveToLocation({ latitude: res.latitude, longitude: res.longitude }) }, fail: (err) => { console.error('定位失败:', err) uni.showToast({ title: '定位失败,请检查权限设置', icon: 'none' }) } }) } }常见问题排查:
- 定位失败:检查是否已获取定位权限,iOS需要在
manifest.json中配置权限描述 - 坐标偏移:确保使用
gcj02坐标系,这是高德地图使用的标准 - 真机无效:部分Android手机需要开启GPS才能准确定位
3. 高级地图功能实现
3.1 撒点功能实现
撒点(标记位置)是地图应用的核心功能之一。uni-app的map组件通过markers属性实现这一功能。
完整撒点实现代码:
data() { return { markers: [] } }, methods: { addMarkers() { // 示例数据 - 实际项目中可能来自API const locations = [ {id: 1, latitude: 39.91469, longitude: 116.40717, title: '王府井'}, {id: 2, latitude: 39.91569, longitude: 116.40817, title: '故宫'}, {id: 3, latitude: 39.91669, longitude: 116.40917, title: '天安门'} ] this.markers = locations.map(item => ({ id: item.id, latitude: item.latitude, longitude: item.longitude, title: item.title, iconPath: '/static/location.png', // 自定义图标 width: 30, height: 30, callout: { // 点击标记时显示的气泡 content: item.title, color: '#ffffff', fontSize: 14, borderRadius: 10, bgColor: '#007AFF', padding: 5, display: 'ALWAYS' } })) } }自定义标记图标技巧:
- 准备透明背景的PNG图标,建议尺寸60×60像素
- 将图标放入项目的
static目录 - 在marker配置中指定
iconPath属性 - 通过
width和height调整显示大小
3.2 绘制路线与区域
绘制路线实现:
data() { return { polyline: [] } }, methods: { drawRoute() { this.polyline = [{ points: [ {latitude: 39.91469, longitude: 116.40717}, {latitude: 39.91569, longitude: 116.40817}, {latitude: 39.91669, longitude: 116.40917} ], color: '#00BFFF', // 线条颜色 width: 6, // 线条宽度 dottedLine: false, // 是否虚线 arrowLine: true // 是否带箭头 }] } }绘制区域实现:
data() { return { polygons: [] } }, methods: { drawArea() { this.polygons = [{ points: [ {latitude: 39.91469, longitude: 116.40717}, {latitude: 39.91569, longitude: 116.40817}, {latitude: 39.91669, longitude: 116.40917} ], strokeWidth: 2, // 边框宽度 strokeColor: '#FF0000', // 边框颜色 fillColor: '#00BFFF33' // 填充颜色(带透明度) }] } }重要提示:绘制区域至少需要3个点才能形成闭合图形,否则会报错。
4. 性能优化与高级技巧
4.1 地图性能优化
随着标记点和图形增多,地图性能可能会下降。以下是一些优化建议:
- 分批加载:当有大量标记点时,不要一次性全部加载
- 按需渲染:只显示当前视野范围内的标记点
- 简化图形:减少多边形和折线的点数
- 使用图片覆盖:对于复杂图形,考虑使用图片覆盖代替矢量绘制
示例代码:分批加载标记点
methods: { loadMarkersInView() { this.mapContext.getRegion({ success: (res) => { const visibleMarkers = this.allMarkers.filter(marker => marker.latitude >= res.southwest.latitude && marker.latitude <= res.northeast.latitude && marker.longitude >= res.southwest.longitude && marker.longitude <= res.northeast.longitude ) this.markers = visibleMarkers } }) } }4.2 坐标转换处理
高德地图使用的是GCJ-02坐标系,而GPS设备通常返回WGS-84坐标。如果你需要处理原始GPS数据,必须进行坐标转换。
坐标转换工具函数:
// 坐标转换工具函数 function transformFromWGSToGCJ(latitude, longitude) { const a = 6378245.0 const ee = 0.00669342162296594323 if (outOfChina(latitude, longitude)) { return {latitude, longitude} } let dLat = transformLat(longitude - 105.0, latitude - 35.0) let dLon = transformLon(longitude - 105.0, latitude - 35.0) const radLat = latitude / 180.0 * Math.PI let magic = Math.sin(radLat) magic = 1 - ee * magic * magic const sqrtMagic = Math.sqrt(magic) dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI) dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI) return { latitude: latitude + dLat, longitude: longitude + dLon } } function outOfChina(lat, lon) { return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271 } function transformLat(x, y) { let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)) ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0 ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0 ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0 return ret } function transformLon(x, y) { let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)) ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0 ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0 ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0 return ret }4.3 自定义地图样式
高德地图支持自定义地图样式,让你的应用与众不同。你可以通过以下步骤实现:
- 访问高德地图的自定义样式平台创建新样式
- 获取样式ID后,在uni-app中通过
map组件的setting属性应用
示例代码:
<map id="myMap" style="width: 100%; height: 80vh" :latitude="latitude" :longitude="longitude" :setting="mapSetting" ></map>data() { return { mapSetting: { style: 'amap://styles/你的样式ID' // 从高德地图自定义平台获取 } } }5. 实战案例:门店地图展示系统
让我们把这些知识点整合到一个实际案例中 - 一个门店地图展示系统。这个系统需要展示多个门店位置,支持路线规划和服务区域划分。
核心功能实现:
- 门店数据加载与展示
async loadStores() { try { const res = await uni.request({ url: '你的API地址/stores', method: 'GET' }) this.stores = res.data this.renderStoreMarkers() } catch (error) { console.error('加载门店数据失败:', error) uni.showToast({ title: '加载门店数据失败', icon: 'none' }) } }, renderStoreMarkers() { this.markers = this.stores.map(store => ({ id: store.id, latitude: store.latitude, longitude: store.longitude, title: store.name, iconPath: '/static/store.png', width: 40, height: 40, callout: { content: `${store.name}\n营业时间:${store.businessHours}`, color: '#333333', fontSize: 12, borderRadius: 4, bgColor: '#FFFFFF', padding: 5, display: 'ALWAYS' } })) }- 路线规划功能
methods: { planRoute(start, end) { // 使用高德地图路径规划API uni.request({ url: 'https://restapi.amap.com/v3/direction/driving', data: { key: '你的高德地图Key', origin: `${start.longitude},${start.latitude}`, destination: `${end.longitude},${end.latitude}`, strategy: 0 // 0:速度优先 }, success: (res) => { if (res.data.status === '1') { const path = res.data.route.paths[0] this.drawRoute(path.steps) } } }) }, drawRoute(steps) { const points = [] steps.forEach(step => { const polyline = step.polyline.split(';') polyline.forEach(point => { const [longitude, latitude] = point.split(',') points.push({ latitude: parseFloat(latitude), longitude: parseFloat(longitude) }) }) }) this.polyline = [{ points: points, color: '#1890FF', width: 6, arrowLine: true }] } }- 服务区域可视化
methods: { showServiceArea(storeId) { const store = this.stores.find(s => s.id === storeId) if (store && store.serviceArea) { this.polygons = [{ points: store.serviceArea, strokeWidth: 2, strokeColor: '#FF6A00', fillColor: '#FF6A0033' }] // 调整地图视野以包含整个服务区域 this.mapContext.includePoints({ points: store.serviceArea, padding: [50, 50, 50, 50] }) } } }在实际项目中,我遇到过几个值得注意的问题:首先是地图初始化时的性能问题,特别是在低端安卓设备上,解决方案是延迟加载非关键地图元素;其次是坐标转换的精度问题,建议使用官方提供的转换服务而非纯前端计算;最后是标记点过多时的卡顿问题,采用聚类标记技术可以有效改善。
