查看: 253|回复: 0

鸿蒙 MapContext 地图轨迹回放与标记动画开发实践

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在鸿蒙项目里,地图组件 Demo 往往只展示静态标记点,但实际开发经常要动态控制地图:移动到指定位置、缩放视野、让标记点动起来做轨迹回放。这些操作都要通过 MapContext 完成。本文结合一个地图上下文 Demo,整理 has.createMapContext 的常用 API、轨迹回放写法和实际踩坑。

一、初始化 MapContext

先通过地图组件 id 获取上下文:
  1. Page({
  2.   onReady() {
  3.     this.mapCtx = has.createMapContext('myMap');
  4.   }
  5. });
复制代码
在 onReady 里调用比较稳妥,此时地图组件已经渲染完成。拿到 mapCtx 后,就能调用各种方法控制地图。

二、获取地图信息

获取中心点经纬度:
  1. this.mapCtx.getCenterLocation({
  2.   success: (res) => {
  3.     console.info(`中心点: ${res.latitude}, ${res.longitude}`);
  4.   }
  5. });
复制代码
返回 gcj02 坐标系经纬度。

获取视野范围:
  1. this.mapCtx.getRegion({
  2.   success: (res) => {
  3.     console.info('西南角:', res.southwest);
  4.     console.info('东北角:', res.northeast);
  5.   }
  6. });
复制代码
返回西南角和东北角经纬度,可判断点是否在当前视野内。

获取缩放级别、旋转角、倾斜角:
  1. this.mapCtx.getScale({
  2.   success: (res) => { console.info('缩放级别:', res.scale); }
  3. });
  4. this.mapCtx.getRotate({
  5.   success: (res) => { console.info('旋转角:', res.rotate); }
  6. });
  7. this.mapCtx.getSkew({
  8.   success: (res) => { console.info('倾斜角:', res.skew); }
  9. });
复制代码
这三个方法用法一致,都是读取地图当前状态参数。

三、移动地图与视野控制

移动到指定位置:
  1. this.mapCtx.moveToLocation({
  2.   longitude: 116.397428,
  3.   latitude: 39.90923,
  4.   success: () => {
  5.     console.info('已移动到天安门');
  6.   }
  7. });
复制代码
如果不传经纬度,会移动到当前定位点。需要地图组件设置 show-location="true",并且有定位权限。

自动缩放包含所有点:
  1. this.mapCtx.includePoints({
  2.   points: [
  3.     { latitude: 31.984, longitude: 118.766 },
  4.     { latitude: 39.909, longitude: 116.397 },
  5.     { latitude: 31.230, longitude: 121.473 }
  6.   ],
  7.   padding: [50, 50, 50, 50],
  8.   success: () => {
  9.     console.info('已包含所有点');
  10.   }
  11. });
复制代码
padding 是坐标点形成的矩形到地图边缘的距离,防止点贴着边缘显示。

设置中心偏移:
  1. this.mapCtx.setCenterOffset({
  2.   offset: [0.5, 0.35],
  3.   success: () => {
  4.     console.info('中心偏移已设置');
  5.   }
  6. });
复制代码
offset 为 [水平, 垂直],范围 0.25~0.75,默认 [0.5, 0.5],改成 [0.5, 0.35] 就是中心点上移。

四、标记动画与轨迹回放

平移标记:
  1. this.mapCtx.translateMarker({
  2.   markerId: 1,
  3.   destination: {
  4.     latitude: 32.0,
  5.     longitude: 118.8
  6.   },
  7.   autoRotate: true,
  8.   duration: 2000,
  9.   animationEnd: () => {
  10.     console.info('平移动画结束');
  11.   }
  12. });
复制代码
autoRotate 设为 true 后,标记会自动转向移动方向,模拟导航效果。

轨迹回放用 moveAlong,可以让标记沿路径移动,适合行程回放、配送追踪:
  1. const path = [
  2.   { latitude: 31.984, longitude: 118.766 },
  3.   { latitude: 31.986, longitude: 118.767 },
  4.   { latitude: 31.988, longitude: 118.769 },
  5.   { latitude: 31.990, longitude: 118.771 },
  6.   { latitude: 31.992, longitude: 118.774 }
  7. ];
  8. this.mapCtx.moveAlong({
  9.   markerId: 1,
  10.   path: path,
  11.   autoRotate: true,
  12.   isMapMoving: true,
  13.   color: '#0A59F7CC',
  14.   width: 6,
  15.   duration: 5000,
  16.   success: () => {
  17.     console.info('轨迹回放开始');
  18.   }
  19. });
复制代码
isMapMoving 为 true 后,地图视角会跟着标记移动,像导航一样;color 和 width 控制轨迹线样式。

五、经纬度转屏幕坐标

叠加自定义 UI 时,需要知道经纬度对应的屏幕位置:
  1. this.mapCtx.toScreenLocation({
  2.   longitude: 118.766,
  3.   latitude: 31.984,
  4.   success: (res) => {
  5.     console.info(`屏幕坐标: x=${res.x}, y=${res.y}`);
  6.   }
  7. });
复制代码
注意:ASCF 运行时 2.0.1 之前返回 px 单位,2.0.1 及以上返回 vp 单位。版本差异会影响叠加层定位,做适配时要留意。

六、动态管理标记

添加标记:
  1. this.mapCtx.addMarkers({
  2.   markers: [
  3.     { id: 100, latitude: 31.99, longitude: 118.77, title: '标记A' },
  4.     { id: 101, latitude: 31.98, longitude: 118.76, title: '标记B' }
  5.   ],
  6.   clear: false,
  7.   success: () => {
  8.     console.info('标记添加成功');
  9.   }
  10. });
复制代码
clear 为 true 会先清空已有标记再添加。

移除标记:
  1. this.mapCtx.removeMarkers({
  2.   markerIds: [100, 101],
  3.   success: () => {
  4.     console.info('标记移除成功');
  5.   }
  6. });
复制代码

设置定位点图标:
  1. this.mapCtx.setLocMarkerIcon({
  2.   iconPath: '/image/custom_location.png',
  3.   success: () => {
  4.     console.info('定位图标已更换');
  5.   }
  6. });
复制代码
可自定义当前位置的蓝色圆点图标。

七、Demo 与三个实际场景

项目里新建了一个地图上下文 Demo,在“接口” -> “位置”分类下可以找到“地图上下文”入口。Demo 包含:获取中心点、视野范围、缩放级别;移动到当前位置、天安门;缩放视野包含南京/北京/上海三点;标记平移动画;轨迹回放;动态添加/移除标记。

场景一:门店定位。用户选择城市后展示该城市所有门店,并自动缩放视野包含所有门店:
  1. Page({
  2.   data: {
  3.     markers: []
  4.   },
  5.   onLoad() {
  6.     this.loadStores('南京');
  7.   },
  8.   loadStores(city) {
  9.     const stores = [
  10.       { id: 1, name: '新街口店', lat: 32.039, lon: 118.787 },
  11.       { id: 2, name: '河西店', lat: 32.022, lon: 118.743 },
  12.       { id: 3, name: '江宁店', lat: 31.953, lon: 118.839 }
  13.     ];
  14.     const markers = stores.map(s => ({
  15.       id: s.id,
  16.       latitude: s.lat,
  17.       longitude: s.lon,
  18.       title: s.name,
  19.       callout: { content: s.name, display: 'ALWAYS' }
  20.     }));
  21.     this.setData({ markers });
  22.     this.mapCtx.includePoints({
  23.       points: stores.map(s => ({ latitude: s.lat, longitude: s.lon })),
  24.       padding: [60, 60, 60, 60]
  25.     });
  26.   }
  27. });
复制代码

场景二:配送员实时位置。外卖或快递场景,配送员位置实时更新,地图跟随移动:
  1. Page({
  2.   data: {
  3.     driverMarker: { id: 999, latitude: 0, longitude: 0 }
  4.   },
  5.   onReady() {
  6.     this.mapCtx = has.createMapContext('deliveryMap');
  7.     this.startTracking();
  8.   },
  9.   startTracking() {
  10.     this.trackingTimer = setInterval(() => {
  11.       const newLat = this.data.driverMarker.latitude + 0.0001;
  12.       const newLon = this.data.driverMarker.longitude + 0.0001;
  13.       this.mapCtx.translateMarker({
  14.         markerId: 999,
  15.         destination: { latitude: newLat, longitude: newLon },
  16.         autoRotate: true,
  17.         duration: 1000
  18.       });
  19.       this.setData({
  20.         'driverMarker.latitude': newLat,
  21.         'driverMarker.longitude': newLon
  22.       });
  23.     }, 2000);
  24.   },
  25.   onUnload() {
  26.     clearInterval(this.trackingTimer);
  27.   }
  28. });
复制代码
这里用 translateMarker 而不是直接更新 markers 数据,可以实现平滑移动动画,避免标记“跳”。

场景三:行程轨迹展示。用户完成一段行程后,在地图上展示轨迹:
  1. Page({
  2.   data: {
  3.     routeMarkers: []
  4.   },
  5.   showTripRoute(routePoints) {
  6.     this.mapCtx.addMarkers({
  7.       clear: true,
  8.       markers: [
  9.         {
  10.           id: 'start',
  11.           latitude: routePoints[0].latitude,
  12.           longitude: routePoints[0].longitude,
  13.           title: '起点',
  14.           callout: { content: '起点', display: 'ALWAYS' }
  15.         },
  16.         {
  17.           id: 'end',
  18.           latitude: routePoints[routePoints.length - 1].latitude,
  19.           longitude: routePoints[routePoints.length - 1].longitude,
  20.           title: '终点',
  21.           callout: { content: '终点', display: 'ALWAYS' }
  22.         }
  23.       ]
  24.     });
  25.     this.mapCtx.includePoints({
  26.       points: routePoints,
  27.       padding: [80, 80, 80, 80]
  28.     });
  29.   }
  30. });
复制代码

八、踩坑与适配建议

1. moveToLocation 需要定位权限。不传经纬度会移动到当前定位点,但没有权限会走 fail 回调。调用前先检查定位权限,或者传默认经纬度兜底。
2. moveAlong 的 duration 最小值是 100ms。如果传小于 100,会被设为 5000ms。想做快速轨迹回放,duration 至少设 100。
3. includePoints 的 padding 单位是像素,不是 rpx。不同设备上同样像素值看起来间距不一样。多屏适配时建议根据设备宽度动态计算 padding。
4. addMarkers 不会自动去重。连续添加同一个 id 的标记会出现两个重叠标记。添加前检查,或用 clear: true 先清空再添加。
5. getCenterLocation 使用网络资源时需要先配置服务器域名。真机调试可能碰到,开发工具里一般没问题。

九、小结

MapContext 方法很多,但常用的是 moveToLocation 移动位置、includePoints 自动缩放、moveAlong 轨迹回放、addMarkers 动态管理标记。掌握这几个,大部分地图场景都能覆盖。轨迹回放场景中,moveAlong 配合 isMapMoving: true 让地图跟随移动,体验不错,适合物流追踪或行程回放类功能。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-22 12:35 , Processed in 0.021538 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部