更新记录
1.4.7(2026-01-24)
修复鸿蒙端再次进入导航失败的问题
1.4.6(2026-01-24)
展示和隐藏缩放按钮
1.4.5(2026-01-24)
三端,增加一键启动导航方法(无终点启动导航)
查看更多平台兼容性
uni-app x(4.87)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | 5.0 | 12 | 12 | × |
pow-amapx
开发文档
UTS 语法 UTS API插件 UTS uni-app兼容模式组件 UTS 标准模式组件 Hello UTS
欢迎试用本插件,觉得合适再考虑购买
已支持:
- 高德sdk,鸿蒙+安卓+ios
定位功能:单次定位+持续定位+后台定位地图功能:控制以及绘制线+绘制圆+绘制多边形+绘制marker,地图上的多个marker可以同时显示气泡搜索功能:路线规划接口,地理信息查询,逆地理信息查询,行驶距离测量导航功能:驾车导航、步行导航、骑行导航、货车导航(鸿蒙端高德本身sdk目前不支持骑行/步行导航)TTS功能: ios 安卓 鸿蒙 三端都支持语音播报功能- 关于后台定位,开始持续定位时,会去申请后台定位权限,申请成功后,即使app退到后台,定位也会继续。如果用户没同意后台定位权限。那就得自己做界面提示用户,让用户去设置界面手动去设置允许后台(始终)定位权限了
- 只支持 uniappx,不支持 uniapp
- 三端接口统一,所有功能,只要调一样的接口就行
- 插件体积小,避免超过打包限制
- 努力增加功能中...
- 有未实现的需求的老板可以点上方进入[进入交流群]按钮定制需求,或者随时沟通
- 如果需要支持 uniapp 可以参考本人另一款插件高德定位、地图、导航全功能,简单易用,支持安卓和iOS
使用方式
- 在插件市场导入本插件到工程中(试用也可以)
- 【鸿蒙】把示例工程根目录中的
harmony-configs复制到自己工程根目录 - 【鸿蒙】鸿蒙不用打自定义基座,直接仿照示例工程中的
index.uvue中的getHarmonyAppId()方法,在自己项目中调用getHarmonyAppId()获取鸿蒙的专属appIdentifier - 申请高德地图的key。(鸿蒙要把
专属appIdentifier粘贴到高德地图key的申请页面) - 【鸿蒙,安卓,iOS】参考示例工程的
index.uvue中的initAmapSDK方法,调用插件的setAmapKey方法 - 【安卓,iOS】打自定义基座(鸿蒙不需要打)
- 【安卓,iOS】运行到自定义基座,【鸿蒙】直接运行
- 【鸿蒙,安卓,iOS】参考示例工程的
index.uvue中的申请定位权限,单次定位,持续定位方法 - 【鸿蒙】鸿蒙平台如果想调用持续后台定位,需要参考示例工程的
App.uvue,在自己工程的App.uvue的onShow调用amapOnAppShow(),在onHide调用amapOnAppHide() - 导航使用方式可以仿照
powamap/pages/index/navi-view.uvue创建一个导航页面,然后仿照powamap/pages/index/navi.uvue来设置导航参数 - 【HBuilderX的bug】【鸿蒙】4.87以及之前的hbx,试用插件鸿蒙端无法运行,不是插件问题,是 hbx 问题,要先这样绕过:把插件中的
项目根目录/uni_modules/pow-amapx/components目录剪切,粘贴到uni_modules之外,让components目录和uni_modules目录平级,变成项目根目录/components,然后就能正常运行了
接口示例
接口功能
<template>
<view class="container">
<scroll-view class="scroll-area" scroll-y="true">
<text class="title">系统功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="jianchadingweiquanxian">检查定位权限</button>
<!-- #ifdef APP-HARMONY -->
<button type="primary" class="btn" @click="reqHarmonyAppId" size='mini'>获取鸿蒙AppId</button>
<!-- #endif -->
<button type="primary" class="btn" size="mini" @click="goToMap">跳转到地图</button>
<button type="primary" class="btn" size="mini" @click="goToNavi">跳转到导航</button>
</view>
<text class="title">定位功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="dancidingwei">单次定位</button>
<button type="primary" class="btn" size="mini" @click="chixudingwei">持续定位</button>
<button type="primary" class="btn" size="mini" @click="jieshuchixudingwei">结束持续定位</button>
</view>
<text class="title">搜索功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="searchPoi">关键词搜索</button>
<button type="primary" class="btn" size="mini" @click="searchNearby">周边搜索</button>
</view>
<view class="content">
<button type="primary" class="btn" size="mini" @click="searchGeocode">地理编码</button>
<button type="primary" class="btn" size="mini" @click="searchRegeocode">逆地理编码</button>
</view>
<text class="title">工具功能</text>
<view class="content">
<button type="primary" class="btn" size="mini" @click="calcDistance">计算距离</button>
<button type="primary" class="btn" size="mini" @click="calcArea">计算面积</button>
<button type="primary" class="btn" size="mini" @click="convertCoord">坐标转换</button>
<button type="primary" class="btn" size="mini" @click="calcDriveDistance">驾车距离测量</button>
</view>
<text class="title">TTS语音播报</text>
<view class="content">
<input class="voice-input" placeholder="输入要播放的文字" :value="ttsText" @input="inputTTSText" />
</view>
<view class="content">
<button type="primary" class="btn" size="mini" @click="readTTS">播放</button>
</view>
</scroll-view>
<!-- 结果显示区域 -->
<view class="result-area">
<text class="result-text">{{resultText}}</text>
</view>
</view>
</template>
<script lang="uts" setup>
import {
setAmapKey,
getAmapLocation,
startAmapLocation,
stopAmapLocation,
checkAndRequestLocationPermission,
searchAmapPoisWithKeywords,
searchAmapPoisNearby,
searchAmapGeocode,
searchAmapRegeocode,
amapLineDistance,
amapCaculateArea,
amapCoordFromOtherCoord,
searchAmapDriveDistance,
amapTTS,
speakRu
} from '@/uni_modules/pow-amapx' // 导入插件内的方法
// 当前位置(用于搜索)
let currentLat = ref(39.91054)
let currentLon = ref(116.398111)
let resultText = ref('点击按钮测试功能...')
let ttsText = ref('我是文字转语音,支持英语,hello')
// #ifdef APP-HARMONY
import {
getHarmonyAppId
} from '@/uni_modules/pow-amapx'
const reqHarmonyAppId = () => {
// 获取鸿蒙AppId , 高德后台要填这个字段
let appid = getHarmonyAppId()
console.log(`appid: ${appid}`)
uni.showModal({
title: '鸿蒙AppId',
content: appid,
confirmText: '复制到剪切板',
cancelText: '取消',
success: res => {
if (res.confirm) {
uni.setClipboardData({
data: appid,
success: () => {
uni.showToast({
title: '已复制到剪切板'
})
}
})
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
}
// #endif
const initAmapSDK = ()=> {
/* setTencentKey
接收4个参数,分别是
1. ioskey
2. 安卓key
3. 鸿蒙key
*/
setAmapKey(
'2d3da96f1736d9403288c6ed95a5e85f',
'72566fdc715020e355f355e4d9b7ceb1',
'dc45c88c0fb6dbd5ffb54cd33475ab48')
}
onReady(() => {
uni.setNavigationBarTitle({
title: '高德demo'
})
initAmapSDK()
})
const jianchadingweiquanxian = () => {
console.log('请求定位权限');
checkAndRequestLocationPermission(res => {
console.log(`定位权限状态:${JSON.stringify(res)}`)
uni.showToast({
title: `请求权限回调:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
const dancidingwei = () => {
uni.showLoading({
title: '正在定位...'
})
resultText.value = '正在定位...'
getAmapLocation({
needAddress: true, //是否需要返回位置描述
}, res => {
uni.hideLoading()
console.log(` 调用方收到定位结果:${JSON.stringify(res)} , typeof res :${typeof res}`)
// 更新当前位置
const lat = res['lat'] as Number | null
const lon = res['lon'] as Number | null
if (lat != null && lon != null) {
currentLat.value = lat!
currentLon.value = lon!
}
// 显示结果
const address = res['address']
const city = res['city']
let text = `定位成功!\n`
text += `经度: ${lon}\n`
text += `纬度: ${lat}\n`
if (city != null) text += `城市: ${city}\n`
if (address != null) text += `地址: ${address}`
resultText.value = text
uni.showToast({
title: `定位成功`,
icon: 'success'
})
})
}
const chixudingwei = ()=>{
resultText.value = '开始持续定位...'
startAmapLocation({
interval: 2000, // 定位间隔,单位毫秒,默认是2000
enableBg: true, // 是否开启后台定位, 默认false
needAddress: true, //是否需要返回位置描述
bgTitle: '定位中', // 后台定位时显示的标题
}, res=>{
console.log(`持续定位结果:${JSON.stringify(res)}`)
// 更新当前位置
const lat = res['lat'] as Number | null
const lon = res['lon'] as Number | null
if (lat != null && lon != null) {
currentLat.value = lat!
currentLon.value = lon!
}
// 显示结果
const address = res['address']
let text = `持续定位中...\n`
text += `经度: ${lon}\n`
text += `纬度: ${lat}\n`
if (address != null) text += `地址: ${address}`
resultText.value = text
})
}
const jieshuchixudingwei = ()=>{
stopAmapLocation({}, res=>{
console.log(`停止持续定位结果:${JSON.stringify(res)}`)
resultText.value = `停止持续定位: ${JSON.stringify(res)}`
uni.showToast({
title: `停止持续定位结果:${JSON.stringify(res)}`,
icon: 'none'
})
})
}
// ========== 搜索功能 ==========
const searchPoi = () => {
resultText.value = '正在搜索POI...'
searchAmapPoisWithKeywords('餐厅', '北京', (res: UTSJSONObject) => {
console.log('POI搜索结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const pois = res['pois'] as Array<UTSJSONObject> | null
if (pois != null && pois.length > 0) {
let text = `找到 ${pois.length} 个结果:\n`
for (let i = 0; i < Math.min(5, pois.length); i++) {
const poi = pois[i]
text += `${i+1}. ${poi['name']}\n`
}
resultText.value = text
} else {
resultText.value = '未找到结果'
}
} else {
resultText.value = `搜索失败: ${errCode}`
}
})
}
const searchNearby = () => {
resultText.value = '正在搜索周边...'
searchAmapPoisNearby(currentLat.value, currentLon.value, 1000, (res: UTSJSONObject) => {
console.log('周边搜索结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const pois = res['pois'] as Array<UTSJSONObject> | null
if (pois != null && pois.length > 0) {
let text = `周边 ${pois.length} 个POI:\n`
for (let i = 0; i < Math.min(5, pois.length); i++) {
const poi = pois[i]
text += `${i+1}. ${poi['name']}\n`
}
resultText.value = text
} else {
resultText.value = '周边无POI'
}
} else {
resultText.value = `搜索失败: ${errCode}`
}
})
}
const searchGeocode = () => {
resultText.value = '正在进行地理编码...'
searchAmapGeocode('天安门', '北京', (res: UTSJSONObject) => {
console.log('地理编码结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const addressList = res['addressList'] as Array<UTSJSONObject> | null
if (addressList != null && addressList.length > 0) {
const addr = addressList[0]
const lat = addr['lat'] ?? ''
const lon = addr['lon'] ?? ''
const name = addr['name'] ?? ''
resultText.value = `地理编码成功!\n地址: ${name}\n经度: ${lon}\n纬度: ${lat}`
} else {
resultText.value = '未找到地理编码结果'
}
} else {
resultText.value = `地理编码失败: ${errCode}`
}
})
}
const searchRegeocode = () => {
resultText.value = '正在进行逆地理编码...'
searchAmapRegeocode(currentLat.value, currentLon.value, (res: UTSJSONObject) => {
console.log('逆地理编码结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const address = res['address'] ?? ''
const city = res['city'] ?? ''
const district = res['district'] ?? ''
resultText.value = `逆地理编码成功!\n地址: ${address}\n城市: ${city}\n区县: ${district}`
} else {
resultText.value = `逆地理编码失败: ${errCode}`
}
})
}
// ========== 工具功能 ==========
const calcDistance = () => {
// 计算当前位置到天安门的距离
const tiananmenLat = 39.9087
const tiananmenLon = 116.3975
const distance = amapLineDistance(currentLat.value, currentLon.value, tiananmenLat, tiananmenLon)
resultText.value = `当前位置到天安门的直线距离:\n${distance.toFixed(2)} 米\n`
}
const calcArea = () => {
// 计算一个区域的面积(左上角到右下角)
const lat1 = 39.92
const lng1 = 116.38
const lat2 = 39.90
const lng2 = 116.42
const area = amapCaculateArea(lat1, lng1, lat2, lng2)
resultText.value = `区域面积:\n${area.toFixed(2)} 平方米`
}
const convertCoord = () => {
// GPS坐标转高德坐标示例
const gpsLat = 39.9087
const gpsLon = 116.3975
const result = amapCoordFromOtherCoord('gps', gpsLat, gpsLon)
const newLat = result['lat']
const newLon = result['lon']
resultText.value = `GPS坐标转换:\n原坐标: ${gpsLon}, ${gpsLat}\n高德坐标: ${newLon}, ${newLat}`
}
const calcDriveDistance = () => {
// 驾车距离测量示例:当前位置到天安门
const tiananmenLat = 39.9087
const tiananmenLon = 116.3975
resultText.value = '正在测量驾车距离...'
searchAmapDriveDistance(currentLat.value, currentLon.value, tiananmenLat, tiananmenLon, (res: UTSJSONObject) => {
console.log('驾车距离测量结果:', JSON.stringify(res))
const errCode = res['errCode']
if (errCode == 0) {
const distance = res['distance'] as Number | null
const duration = res['duration'] as Number | null
let text = `驾车距离测量成功!\n`
text += `起点: ${currentLon.value}, ${currentLat.value}\n`
text += `终点: ${tiananmenLon}, ${tiananmenLat} (天安门)\n`
if (distance != null) {
text += `驾车距离: ${distance} 米 \n`
}
if (duration != null) {
const minutes = Math.floor(duration / 60)
const seconds = duration! % 60
text += `预计耗时: ${minutes} 分 ${seconds} 秒`
}
resultText.value = text
} else {
resultText.value = `驾车距离测量失败: ${res['errMsg'] ?? errCode}`
}
})
}
const goToMap = () => {
uni.navigateTo({
url: './map'
})
}
const goToNavi = () => {
uni.navigateTo({
url: './navi'
})
}
// ========== TTS 语音播报 ==========
const inputTTSText = (e: InputEvent) => {
ttsText.value = e.detail.value
}
const readTTS = () => {
if (ttsText.value == '') {
uni.showToast({
title: '请输入播放文字',
icon: 'error'
})
return
}
amapTTS(ttsText.value)
resultText.value = `正在播放中文: ${ttsText.value}`
}
</script>
<style lang="scss">
.title {
font-size: 18px;
font-weight: bold;
color: #000;
margin-top: 20px;
margin-left: 10px;
}
.container {
display: flex;
flex-direction: column;
width: 750rpx;
height: 100%;
}
.scroll-area {
flex: 1;
}
.content {
margin-top: 10px;
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
flex-wrap: wrap;
}
.btn {
margin-left: 10px;
margin-top: 5px;
}
.voice-input {
flex: 1;
border-radius: 4px;
border-width: 1px;
border-style: solid;
border-color: #ccc;
margin-top: 10px;
margin-left: 10px;
margin-right: 10px;
padding: 8px;
font-size: 14px;
}
.result-area {
height: 100px;
background-color: #f5f5f5;
padding: 10px;
overflow: hidden;
}
.result-text {
font-size: 14px;
color: #333;
line-height: 1.6;
}
</style>
地图功能
<template>
<view class="container">
<pow-amapx ref="mapRef" class="native-map" :center='mapCenter' :zoomLevel='mapZoomLevel' @load="onMapLoad"
@mapLoaded="onMapLoaded" @mapRendered="onMapRendered" @markerClick="onMarkerClick"
@markerDrag="onMarkerDrag" @cameraChange="onCameraChange"
@cameraChangeStart="onCameraChangeStart"></pow-amapx>
<!-- 按钮区域可滚动 -->
<scroll-view class="controls-area" scroll-y="true">
<!-- 地图控制按钮 -->
<view class="section">
<text class="section-title">地图控制</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="moveCenter">移动中心点</button>
<button class="btn" size="mini" type="primary" @click="zoomIn">放大</button>
<button class="btn" size="mini" type="primary" @click="zoomOut">缩小</button>
<button class="btn" size="mini" type="primary" @click="getMapInfo">获取地图信息</button>
<button class="btn" size="mini" type="warn" @click="locateToCurrent">归位</button>
</view>
<view class="btns">
<button class="btn" size="mini" type="default" @click="setMapTypeNormal">普通地图</button>
<button class="btn" size="mini" type="default" @click="setMapTypeSatellite">卫星地图</button>
<button class="btn" size="mini" type="default" @click="setMapTypeNight">夜间地图</button>
<button class="btn" size="mini" type="default" @click="setMapTypeNavi">导航地图</button>
</view>
<view class="btns">
<button class="btn" size="mini" :type="trafficEnabled ? 'warn' : 'primary'"
@click="toggleTraffic">{{trafficEnabled ? '关闭路况' : '开启路况'}}</button>
<button class="btn" size="mini" :type="compassEnabled ? 'warn' : 'primary'"
@click="toggleCompass">{{compassEnabled ? '隐藏指南针' : '显示指南针'}}</button>
<button class="btn" size="mini" :type="scaleEnabled ? 'warn' : 'primary'"
@click="toggleScale">{{scaleEnabled ? '隐藏比例尺' : '显示比例尺'}}</button>
</view>
</view>
<!-- 绘制功能按钮 -->
<view class="section">
<text class="section-title">绘制功能</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="drawPolyline">画折线</button>
<button class="btn" size="mini" type="warn" @click="removePolyline">删除折线</button>
<button class="btn" size="mini" type="primary" @click="drawCircle">画圆形</button>
<button class="btn" size="mini" type="warn" @click="removeCircle">删除圆形</button>
</view>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="drawPolygon">画多边形</button>
<button class="btn" size="mini" type="warn" @click="removePolygon">删除多边形</button>
<button class="btn" size="mini" type="primary" @click="drawTexturedPolyline">画纹理线</button>
<button class="btn" size="mini" type="default" @click="clearAllOverlays">清除所有</button>
</view>
</view>
<!-- Marker 功能按钮 -->
<view class="section">
<text class="section-title">Marker 功能</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="addMarkers">添加Markers</button>
<button class="btn" size="mini" type="warn" @click="removeAllMarkers">删除所有Markers</button>
<button class="btn" size="mini" type="default" @click="zoomToFitMarkers">缩放包含所有Markers</button>
</view>
</view>
<!-- 手势控制按钮 -->
<view class="section">
<text class="section-title">手势控制</text>
<view class="btns">
<button class="btn" size="mini" :type="zoomGestureEnabled ? 'warn' : 'primary'"
@click="toggleZoomGesture">{{zoomGestureEnabled ? '禁止缩放' : '允许缩放'}}</button>
<button class="btn" size="mini" :type="scrollGestureEnabled ? 'warn' : 'primary'"
@click="toggleScrollGesture">{{scrollGestureEnabled ? '禁止拖拽' : '允许拖拽'}}</button>
</view>
<view class="btns">
<button class="btn" size="mini" :type="rotateGestureEnabled ? 'warn' : 'primary'"
@click="toggleRotateGesture">{{rotateGestureEnabled ? '禁止旋转' : '允许旋转'}}</button>
<button class="btn" size="mini" :type="tiltGestureEnabled ? 'warn' : 'primary'"
@click="toggleTiltGesture">{{tiltGestureEnabled ? '禁止倾斜' : '允许倾斜'}}</button>
</view>
</view>
<!-- 位置功能按钮 -->
<view class="section">
<text class="section-title">位置功能</text>
<view class="btns">
<button class="btn" size="mini" :type="myLocationEnabled ? 'warn' : 'primary'"
@click="toggleMyLocation">{{myLocationEnabled ? '隐藏位置' : '显示位置'}}</button>
<button class="btn" size="mini" :type="followMode ? 'warn' : 'primary'"
@click="toggleFollowMode">{{followMode ? '取消跟随' : '跟随模式'}}</button>
</view>
</view>
<!-- 路径规划按钮 -->
<view class="section">
<text class="section-title">路径规划</text>
<view class="btns">
<button class="btn" size="mini" type="primary" @click="searchDriveRoute">驾车路径规划</button>
<button class="btn" size="mini" type="primary" @click="searchRideRoute">骑行路径规划</button>
<button class="btn" size="mini" type="primary" @click="searchWalkRoute">步行路径规划</button>
<button class="btn" size="mini" type="warn" @click="clearRoutePolyline">清除规划路线</button>
</view>
</view>
<!-- 结果显示区域 -->
<view class="result-area">
<text class="result-text">{{resultText}}</text>
</view>
</scroll-view>
</view>
</template>
<script>
import lineTextureData from '../../static/linetexture.json'
import { searchAmapDriveRoute, searchAmapRideRoute, searchAmapWalkRoute, getAmapLocation, getFileLocalPath } from '@/uni_modules/pow-amapx'
export default {
data() {
return {
mapCenter: '116.398111,39.91054',
mapZoomLevel: 12,
resultText: '点击按钮测试地图控制功能...',
trafficEnabled: false,
compassEnabled: false,
scaleEnabled: true,
// 手势控制状态
zoomGestureEnabled: true,
scrollGestureEnabled: true,
rotateGestureEnabled: true,
tiltGestureEnabled: true,
// 位置功能状态
myLocationEnabled: false,
followMode: false,
// 路径规划起终点
routeStartPoint: { lat: 39.909187, lng: 116.397455 }, // 天安门
routeEndPoint: { lat: 39.984120, lng: 116.307484 }, // 北京大学
routePolylineId: 'route_polyline_001',
// 绘制数据
polylineData: {
id: 'polyline001',
points: [
'116.362209,39.887487',
'116.422897,39.878002',
'116.372105,39.90651',
'116.428945,39.89663'
],
width: 10,
color: '#FF0000',
isDottedLine: false
},
circleData: {
id: 'circle001',
lat: 39.91054,
lng: 116.398111,
radius: 1000,
lineWidth: 5,
strokeColor: '#FF0000',
fillColor: '#50FF0000'
},
polygonData: {
id: 'polygon001',
points: [
'116.404,39.915',
'116.408,39.917',
'116.412,39.919',
'116.412,39.921',
'116.410,39.923',
'116.406,39.923',
'116.404,39.921'
],
lineWidth: 5,
strokeColor: '#0000FF',
fillColor: '#500000FF'
},
texturedPolylineData: {
id: 'texturedPolyline001',
points: [
'116.352209,39.897487',
'116.402897,39.888002',
'116.362105,39.91651',
'116.418945,39.90663',
'116.438945,39.92663'
],
width: 20,
texture: lineTextureData.imageBase64
},
markersData: [
{
id: 'marker001',
lat: 39.909187,
lng: 116.397455,
title: '天安门',
subtitle: '北京市中心',
anchor: 'bottomCenter',
icon: 'https://s11.ax1x.com/2023/03/17/ppGtKhD.png', //支持网络图片和 base64 字符串,免费生成网络图片的地址 imgse.com
draggable: true,
rotate: 45,
iconWidth: 40,
iconHeight: 40,
callout: {
content: '天安门广场',
textColor: '#ffffff',
fontSize: 14,
borderRadius: 8,
borderColor: '#007AFF',
borderWidth: 1,
bgColor: '#007AFF',
padding: 8,
leftIcon: {
url: 'https://a.amap.com/jsapi_demos/static/demo-center/icons/dir-marker.png',
width: 16,
height: 16
}
}
},
{
id: 'marker002',
lat: 39.915,
lng: 116.404,
title: '故宫',
anchor: 'center',
callout: {
content: '故宫博物院',
textColor: '#333333',
fontSize: 12,
borderRadius: 5,
bgColor: '#FFD700',
padding: 5,
rightIcon: {
url: 'https://a.amap.com/jsapi_demos/static/demo-center/icons/dir-via-marker.png',
width: 16,
height: 16
}
}
},
{
id: 'marker003',
lat: 39.905,
lng: 116.410,
title: '圆角图标示例',
anchor: 'center',
icon: 'https://s41.ax1x.com/2026/01/05/pZaq0a9.jpg',
iconWidth: 50,
iconHeight: 50,
iconRadius: 25 // 设置圆角半径,值为图标宽高一半时显示为圆形
}
]
}
},
methods: {
// ========== 地图事件 ==========
onMapLoad() {
console.log('地图组件加载完成1')
this.resultText = '地图组件加载完成'
},
onMapLoaded(e : UniNativeViewEvent) {
console.log('地图已加载')
this.resultText = '地图已加载'
},
onMapRendered(e : UniNativeViewEvent) {
console.log('地图已渲染')
this.resultText = '地图已渲染'
},
// ========== 地图控制 ==========
moveCenter() {
const lat = 39.9 + Math.random() * 0.1
const lon = 116.3 + Math.random() * 0.2
this.mapCenter = `${lon},${lat}`
this.resultText = `中心点移动到: ${lon.toFixed(6)}, ${lat.toFixed(6)}`
},
zoomIn() {
if (this.mapZoomLevel < 20) {
this.mapZoomLevel += 1
this.resultText = `缩放级别: ${this.mapZoomLevel}`
}
},
zoomOut() {
if (this.mapZoomLevel > 3) {
this.mapZoomLevel -= 1
this.resultText = `缩放级别: ${this.mapZoomLevel}`
}
},
getMapInfo() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const center = mapRef.$callMethod('getCenter') as UTSJSONObject
const zoom = mapRef.$callMethod('getZoomLevel') as number
const lat = center['lat'] as number
const lon = center['lon'] as number
this.resultText = `当前地图信息:\n中心点: ${lon.toFixed(6)}, ${lat.toFixed(6)}\n缩放级别: ${zoom.toFixed(1)}`
}
},
setMapTypeNormal() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 1)
this.resultText = '已切换到普通地图'
}
},
setMapTypeSatellite() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 2)
this.resultText = '已切换到卫星地图'
}
},
setMapTypeNight() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 3)
this.resultText = '已切换到夜间地图'
}
},
setMapTypeNavi() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setMapType', 4)
this.resultText = '已切换到导航地图'
}
},
toggleTraffic() {
this.trafficEnabled = !this.trafficEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('showTraffic', this.trafficEnabled)
this.resultText = this.trafficEnabled ? '已开启实时路况' : '已关闭实时路况'
}
},
toggleCompass() {
this.compassEnabled = !this.compassEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setCompassEnabled', this.compassEnabled)
this.resultText = this.compassEnabled ? '已显示指南针' : '已隐藏指南针'
}
},
toggleScale() {
this.scaleEnabled = !this.scaleEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setScaleControlsEnabled', this.scaleEnabled)
this.resultText = this.scaleEnabled ? '已显示比例尺' : '已隐藏比例尺'
}
},
// ========== 绘制功能 ==========
drawPolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawPolyline', JSON.stringify(this.polylineData))
this.resultText = '已绘制折线'
}
},
removePolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removePolyline', '{"id":"polyline001"}')
this.resultText = '已删除折线'
}
},
drawCircle() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawCircle', JSON.stringify(this.circleData))
this.resultText = '已绘制圆形'
}
},
removeCircle() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removeCircle', '{"id":"circle001"}')
this.resultText = '已删除圆形'
}
},
drawPolygon() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawPolygon', JSON.stringify(this.polygonData))
this.resultText = '已绘制多边形'
}
},
removePolygon() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removePolygon', '{"id":"polygon001"}')
this.resultText = '已删除多边形'
}
},
drawTexturedPolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('drawTexturedPolyline', JSON.stringify(this.texturedPolylineData))
this.resultText = '已绘制纹理折线'
}
},
clearAllOverlays() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removeAllPolylines')
mapRef.$callMethod('removeAllCircles')
mapRef.$callMethod('removeAllPolygons')
this.resultText = '已清除所有覆盖物'
}
},
// ========== Marker 功能 ==========
addMarkers() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
// 获取本地图片路径的方法,从插件导入使用
getFileLocalPath(
'../../static/ball.png', //资源在 hbx 中的相对路径
(localIconPath : string) => { //获取到的资源原生本地路径
const marker004 = {
id: 'marker004',
lat: 39.925,
lng: 116.395,
title: '本地图标示例',
anchor: 'center',
icon: localIconPath, //只能支持原生用的本地路径
iconWidth: 40,
iconHeight: 40
}
const markersDataWithLocal = [...this.markersData, marker004]
mapRef.$callMethod('addMarkers', JSON.stringify(markersDataWithLocal))
this.resultText = '已添加Markers(marker004使用本地图片)'
},
(err : any) => {
this.resultText = '获取本地图片路径失败: ' + JSON.stringify(err)
}
)
}
},
removeAllMarkers() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removeAllMarkers')
this.resultText = '已删除所有Markers'
}
},
onMarkerClick(e : UniNativeViewEvent) {
console.log('Marker点击:', JSON.stringify(e.detail))
this.resultText = 'Marker点击: ' + JSON.stringify(e.detail)
},
onMarkerDrag(e : UniNativeViewEvent) {
console.log('Marker拖拽:', JSON.stringify(e.detail))
this.resultText = 'Marker拖拽: ' + JSON.stringify(e.detail)
},
// ========== 视角改变回调 ==========
onCameraChange(e : UniNativeViewEvent) {
console.log('视角改变完成:', JSON.stringify(e.detail))
// 解析回调数据
try {
const detailStr = e.detail['detail'] as string
if (detailStr != null) {
const detail = JSON.parseObject(detailStr)
if (detail != null) {
const centerLat = detail['centerLat'] as number
const centerLng = detail['centerLng'] as number
const zoomLevel = detail['zoomLevel'] as number
const rotation = detail['rotation'] as number
const pitch = detail['pitch'] as number
this.resultText = `视角改变完成:\n中心点: ${centerLng.toFixed(6)}, ${centerLat.toFixed(6)}\n缩放: ${zoomLevel.toFixed(1)}\n旋转: ${rotation.toFixed(1)}°\n俯仰: ${pitch.toFixed(1)}°`
}
}
} catch (err) {
console.log('解析视角数据失败:', err)
}
},
onCameraChangeStart(e : UniNativeViewEvent) {
console.log('视角开始改变:', JSON.stringify(e.detail))
},
// ========== 缩放以包含 Markers ==========
zoomToFitMarkers() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
// 构造参数,包含所有 marker 的坐标
const params = {
markers: this.markersData, // 使用已添加的 markers 数据
paddingTop: 5, // 上边距,单位像素
paddingBottom: 5, // 下边距
paddingLeft: 5, // 左边距
paddingRight: 5, // 右边距
animated: true // 是否使用动画
}
mapRef.$callMethod('setVisibleIncludeMarkers', JSON.stringify(params))
this.resultText = '已缩放地图以包含所有Markers'
}
},
// ========== 手势控制 ==========
toggleZoomGesture() {
this.zoomGestureEnabled = !this.zoomGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setZoomGesturesEnabled', this.zoomGestureEnabled)
this.resultText = this.zoomGestureEnabled ? '已允许缩放手势' : '已禁止缩放手势'
}
},
toggleScrollGesture() {
this.scrollGestureEnabled = !this.scrollGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setScrollGesturesEnabled', this.scrollGestureEnabled)
this.resultText = this.scrollGestureEnabled ? '已允许拖拽手势' : '已禁止拖拽手势'
}
},
toggleRotateGesture() {
this.rotateGestureEnabled = !this.rotateGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setRotateGesturesEnabled', this.rotateGestureEnabled)
this.resultText = this.rotateGestureEnabled ? '已允许旋转手势' : '已禁止旋转手势'
}
},
toggleTiltGesture() {
this.tiltGestureEnabled = !this.tiltGestureEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('setTiltGesturesEnabled', this.tiltGestureEnabled)
this.resultText = this.tiltGestureEnabled ? '已允许倾斜手势' : '已禁止倾斜手势'
}
},
// ========== 位置功能 ==========
toggleMyLocation() {
this.myLocationEnabled = !this.myLocationEnabled
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const params = {
show: this.myLocationEnabled,
updatingMapCenter: this.followMode
}
mapRef.$callMethod('showMyLocation', JSON.stringify(params))
this.resultText = this.myLocationEnabled ? '已显示自身位置' : '已隐藏自身位置'
}
},
toggleFollowMode() {
this.followMode = !this.followMode
// 如果位置已显示,更新跟随模式
if (this.myLocationEnabled) {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
const params = {
show: true,
updatingMapCenter: this.followMode
}
mapRef.$callMethod('showMyLocation', JSON.stringify(params))
}
}
this.resultText = this.followMode ? '已开启跟随模式(地图跟随位置移动)' : '已关闭跟随模式'
},
// ========== 路径规划功能 ==========
searchDriveRoute() {
this.resultText = '正在搜索驾车路线...'
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
searchAmapDriveRoute(
latStart,
lngStart,
latEnd,
lngEnd,
0, // strategy: 0-速度优先
(result : UTSJSONObject) => {
console.log('驾车路径规划结果: ', JSON.stringify(result))
this.handleRouteResult(result, '驾车')
}
)
},
searchRideRoute() {
this.resultText = '正在搜索骑行路线...'
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
searchAmapRideRoute(
latStart,
lngStart,
latEnd,
lngEnd,
(result : UTSJSONObject) => {
console.log('骑行路径规划结果:', JSON.stringify(result))
this.handleRouteResult(result, '骑行')
}
)
},
searchWalkRoute() {
this.resultText = '正在搜索步行路线...'
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
searchAmapWalkRoute(
latStart,
lngStart,
latEnd,
lngEnd,
(result : UTSJSONObject) => {
console.log('步行路径规划结果:', JSON.stringify(result))
this.handleRouteResult(result, '步行')
}
)
},
handleRouteResult(result : UTSJSONObject, routeType : string) {
const errCode = result['errCode'] as number
if (errCode !== 0) {
this.resultText = `${routeType}路径规划失败,错误码: ${errCode}`
return
}
const distance = result['distance'] as number
const duration = result['duration'] as number
const polyline = result['polyline'] as string[]
const taxiCost = result['taxiCost'] as number
// 显示规划结果信息
let info = `${routeType}路径规划成功\n距离: ${(distance / 1000).toFixed(2)} 公里\n预计耗时: ${Math.ceil(duration / 60)} 分钟`
if (taxiCost != null && taxiCost > 0) {
info += `\n预估打车费: ¥${taxiCost.toFixed(0)}`
}
info += `\n路线点数: ${polyline.length}`
this.resultText = info
// 在地图上绘制路线
if (polyline.length > 0) {
this.drawRouteOnMap(polyline, routeType)
}
},
drawRouteOnMap(polyline : string[], routeType : string) {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef == null) return
// 根据路线类型设置不同颜色
let color = '#3498db' // 默认蓝色
if (routeType === '驾车') {
color = '#e74c3c' // 红色
} else if (routeType === '骑行') {
color = '#2ecc71' // 绿色
} else if (routeType === '步行') {
color = '#9b59b6' // 紫色
}
// 先删除之前的路线和起终点marker
mapRef.$callMethod('removePolyline', JSON.stringify({ id: this.routePolylineId }))
mapRef.$callMethod('removeSomeMarkers', JSON.stringify([{ id: 'route_start_marker' }, { id: 'route_end_marker' }]))
// 绘制新路线
const routeData = {
id: this.routePolylineId,
points: polyline,
width: 12,
color: color,
isDottedLine: false
}
mapRef.$callMethod('drawPolyline', JSON.stringify(routeData))
// 添加起终点marker
let latStart = this.routeStartPoint['lat'] as number
let lngStart = this.routeStartPoint['lng'] as number
let latEnd = this.routeEndPoint['lat'] as number
let lngEnd = this.routeEndPoint['lng'] as number
const routeMarkers = [
{
id: 'route_start_marker',
lat: latStart,
lng: lngStart,
title: '起点',
icon: 'https://webapi.amap.com/theme/v1.3/markers/n/start.png',
iconWidth: 19,
iconHeight: 31,
anchor: 'bottomCenter'
},
{
id: 'route_end_marker',
lat: latEnd,
lng: lngEnd,
title: '终点',
icon: 'https://webapi.amap.com/theme/v1.3/markers/n/end.png',
iconWidth: 19,
iconHeight: 31,
anchor: 'bottomCenter'
}
]
mapRef.$callMethod('addMarkers', JSON.stringify(routeMarkers))
// 延迟设置可视区域,确保 markers 已添加完成后再调整视野
if (polyline.length >= 2) {
setTimeout(() => {
const mapRefInner = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRefInner != null) {
const params = {
markers: routeMarkers,
paddingTop: 80,
paddingBottom: 80,
paddingLeft: 40,
paddingRight: 40,
animated: true
}
mapRefInner.$callMethod('setVisibleIncludeMarkers', JSON.stringify(params))
}
}, 300)
}
},
clearRoutePolyline() {
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('removePolyline', JSON.stringify({ id: this.routePolylineId }))
mapRef.$callMethod('removeSomeMarkers', JSON.stringify([{ id: 'route_start_marker' }, { id: 'route_end_marker' }]))
this.resultText = '已清除规划路线'
}
},
// ========== 归位功能 ==========
locateToCurrent() {
this.resultText = '正在获取当前位置...'
getAmapLocation({
needAddress: true,
desiredAccuracy: 100,
}, (res : UTSJSONObject) => {
const lat = res['lat'] as number | null
const lon = res['lon'] as number | null
if (lat != null && lon != null) {
// 调用地图组件的 updateCenter 方法设置中心点
const mapRef = this.$refs['mapRef'] as ComponentPublicInstance
if (mapRef != null) {
mapRef.$callMethod('updateCenter', `${lon},${lat}`)
}
const address = res['address'] as string | null
const city = res['city'] as string | null
let text = `已归位到当前位置: ${lon.toFixed(6)}, ${lat.toFixed(6)}`
if (city != null) text += `\n城市: ${city}`
if (address != null) text += `\n地址: ${address}`
this.resultText = text
} else {
this.resultText = '定位失败,请检查定位权限'
}
})
}
}
}
</script>
<style>
.section {
padding: 10rpx 20rpx;
background-color: #f8f8f8;
margin-top: 10rpx;
}
.section-title {
font-size: 28rpx;
color: #666;
margin-bottom: 10rpx;
}
.btn {
margin-left: 10rpx;
margin-top: 10rpx;
}
.btns {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding-top: 10rpx;
padding-bottom: 10rpx;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.native-map {
height: 35%;
width: 100%;
}
.controls-area {
flex: 1;
}
.result-area {
flex: 1;
background-color: #fff;
padding: 20rpx;
margin: 10rpx;
border-radius: 10rpx;
border: 1px solid #eee;
}
.result-text {
font-size: 26rpx;
color: #333;
line-height: 1.6;
}
</style>
导航组件方法说明
| 方法名 | 参数 | 说明 |
|---|---|---|
| calculateDriveRoute | JSON字符串 | 规划驾车路线 |
| calculateWalkRoute | JSON字符串 | 规划步行路线 |
| calculateRideRoute | JSON字符串 | 规划骑行路线 |
| calculateTruckRoute | JSON字符串 | 规划货车路线 |
| startGPSNavi | 无 | 开始GPS实时导航 |
| startEmulatorNavi | 无 | 开始模拟导航 |
| stopNavi | 无 | 停止导航 |
| destroy | 无 | 销毁导航组件 |
路线规划参数说明
{
"startLat": 39.909187, // 起点纬度
"startLng": 116.397455, // 起点经度
"endLat": 39.917839, // 终点纬度
"endLng": 116.397029, // 终点经度
"naviType": "drive", // 导航类型: drive/walk/ride/truck
"naviMode": "gps", // 导航模式: gps/emulator
"strategy": 0, // 路线策略(可选)
// 货车专用参数(仅 naviType 为 truck 时有效)
"truckSize": 2, // 车型: 1-微型, 2-轻型, 3-中型, 4-重型
"truckHeight": 2.5, // 车高(米)
"truckWidth": 2.0, // 车宽(米)
"truckLoad": 5.0, // 载重(吨)
"truckWeight": 10.0, // 车重(吨)
"truckAxis": 2 // 轴数
}
导航回调事件说明
| 事件名 | 说明 | 返回数据 |
|---|---|---|
| initNavi | 导航初始化 | { success: boolean } |
| calculateRoute | 路线规划结果 | { success: boolean, status: string } |
| startNavi | 导航开始 | { type: 'GPS'/'EMULATOR' } |
| stopNavi | 导航停止 | { status: string } |
| arriveDestination | 到达目的地 | { status: 'arrived' } |
| naviInfoUpdate | 导航信息更新 | { curRoadName, nextRoadName, remainDistance, remainTime } |
| locationChange | 位置变化 | { lat, lng, speed, bearing } |
| recalculateRoute | 重新规划路线 | { reason: 'yaw'/'congestion' } |
| goBack | 用户点击退出按钮 | { status: string } |
| error | 错误信息 | { message: string } |
| warning | 警告信息 | { message: string } |
平台差异说明
| 功能 | Android | iOS | HarmonyOS |
|---|---|---|---|
| 驾车导航 | ✅ | ✅ | ✅ |
| 步行导航 | ✅ | ✅ | ⚠️ 自动切换为驾车 |
| 骑行导航 | ✅ | ✅ | ⚠️ 自动切换为驾车 |
| 货车导航 | ✅ | ✅ | ✅ |
| GPS实时导航 | ✅ | ✅ | ✅ |
| 模拟导航 | ✅ | ✅ | ✅ |
| 语音播报 | ✅ | ✅ | ✅ |
注意:鸿蒙端 AMap SDK 1.0 版本暂不支持步行和骑行导航,选择这两种类型时会自动切换为驾车导航并给出提示。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 1857
赞赏 0
下载 13666278
赞赏 1851
赞赏
京公网安备:11010802035340号