写点什么

鸿蒙运动开发实战:打造 Keep 式轨迹播放效果

  • 2025-05-28
    北京
  • 本文字数:3171 字

    阅读完需:约 10 分钟

前言


在运动类应用中,轨迹播放效果是提升用户体验的关键功能之一。它不仅能直观展示用户的运动路线,还能通过动态效果增强运动的趣味性。Keep 作为一款知名的运动健身应用,其轨迹播放效果深受用户喜爱。那么,如何在鸿蒙系统中开发出类似 Keep 的轨迹播放效果呢?本文将通过实际代码案例,深入解析实现这一功能的关键步骤和技术要点。


效果:



一、核心功能拆解


要实现类似 Keep 的轨迹播放效果,我们需要完成以下几个核心功能:


• 动态轨迹播放:通过定时器和动画效果,实现轨迹的动态播放,模拟用户运动过程。


• 地图交互:在地图上绘制轨迹,并根据播放进度更新地图中心点和旋转角度。


二、动态轨迹播放


1.播放逻辑


通过定时器和动画效果实现轨迹的动态播放。以下是播放轨迹的核心代码:


private playTrack() {  // 如果已经在播放,则停止  if (this.playTimer) {    this.mapController?.removeOverlay(this.polyline);    clearInterval(this.playTimer);    this.playTimer = undefined;    if (this.animationTimer) {      clearInterval(this.animationTimer);    }    if (this.movingMarker) {      this.mapController?.removeOverlay(this.movingMarker);      this.movingMarker = undefined;    }    this.currentPointIndex = 0;    return;  }
// 创建动态位置标记 this.movingMarker = new Marker({ position: this.trackPoints[0], icon: new ImageEntity("rawfile://images/ic_run_detail_start.png"), isJoinCollision: SysEnum.CollisionBehavior.NOT_COLLIDE, located: SysEnum.Located.CENTER }); this.mapController?.addOverlay(this.movingMarker);
// 开始播放 this.playTimer = setInterval(() => { this.currentPointIndex++; if (this.currentPointIndex >= this.trackPoints.length) { clearInterval(this.playTimer); this.playTimer = undefined; this.currentPointIndex = 0; if (this.movingMarker) { this.mapController?.removeOverlay(this.movingMarker); this.movingMarker = undefined; } return; }
// 更新动态位置标记位置,使用setInterval实现平滑移动 if (this.movingMarker && this.currentPointIndex < this.trackPoints.length - 1) { const currentPoint = this.trackPoints[this.currentPointIndex]; const nextPoint = this.trackPoints[this.currentPointIndex + 1]; let animationProgress = 0;
// 清除之前的动画定时器 if (this.animationTimer) { clearInterval(this.animationTimer); }
// 创建新的动画定时器,每10ms更新一次位置 this.animationTimer = setInterval(() => { animationProgress += 0.1; // 每次增加0.1的进度
if (animationProgress >= 1) { clearInterval(this.animationTimer); this.animationTimer = undefined; this.movingMarker?.setPosition(new LatLng(nextPoint.lat, nextPoint.lng)); } else { const interpolatedLat = currentPoint.lat + (nextPoint.lat - currentPoint.lat) * animationProgress; const interpolatedLng = currentPoint.lng + (nextPoint.lng - currentPoint.lng) * animationProgress; this.movingMarker?.setPosition(new LatLng(interpolatedLat, interpolatedLng)); } }, 10); // 每10ms执行一次 }
// 绘制当前轨迹线段 const currentPoints = this.trackPoints.slice(0, this.currentPointIndex + 1); const currentColors = PathGradientTool.getPathColors(this.record!.points.slice(0, this.currentPointIndex + 1), 100);
if (this.polyline) { this.mapController?.removeOverlay(this.polyline); this.polyline.remove(); this.polyline.destroy(); }
this.polyline = new Polyline({ points: currentPoints, width: 5, join: SysEnum.LineJoinType.ROUND, cap: SysEnum.LineCapType.ROUND, isGradient: true, colorList: currentColors! }); this.mapController?.addOverlay(this.polyline);
// 更新地图中心点和旋转角度 let bearing = 0; if (this.currentPointIndex < this.trackPoints.length - 1) { const currentPoint = this.trackPoints[this.currentPointIndex]; const nextPoint = this.trackPoints[this.currentPointIndex + 1]; bearing = Math.atan2( nextPoint.lat - currentPoint.lat, nextPoint.lng - currentPoint.lng ) * 180 / Math.PI; bearing = (bearing + 360) % 360; bearing = (360 - bearing + 90) % 360; }
this.mapController?.mapStatus.setRotate(bearing).setOverlooking(90).setCenterPoint(new LatLng(this.trackPoints[this.currentPointIndex].lat, this.trackPoints[this.currentPointIndex].lng)).refresh(); }, 100); // 每100ms移动一次}
复制代码


2.动画效果


通过定时器和线性插值实现动态轨迹的平滑移动效果。以下是动画效果的核心代码:


if (this.movingMarker && this.currentPointIndex < this.trackPoints.length - 1) {  const currentPoint = this.trackPoints[this.currentPointIndex];  const nextPoint = this.trackPoints[this.currentPointIndex + 1];  let animationProgress = 0;
// 清除之前的动画定时器 if (this.animationTimer) { clearInterval(this.animationTimer); }
// 创建新的动画定时器,每10ms更新一次位置 this.animationTimer = setInterval(() => { animationProgress += 0.1; // 每次增加0.1的进度
if (animationProgress >= 1) { clearInterval(this.animationTimer); this.animationTimer = undefined; this.movingMarker?.setPosition(new LatLng(nextPoint.lat, nextPoint.lng)); } else { const interpolatedLat = currentPoint.lat + (nextPoint.lat - currentPoint.lat) * animationProgress; const interpolatedLng = currentPoint.lng + (nextPoint.lng - currentPoint.lng) * animationProgress; this.movingMarker?.setPosition(new LatLng(interpolatedLat, interpolatedLng)); } }, 10); // 每10ms执行一次}
复制代码


三、地图交互


1.地图中心点和旋转角度更新


在播放轨迹的过程中,动态更新地图的中心点和旋转角度,以确保用户始终能看到当前播放的位置。以下是更新地图中心点和旋转角度的代码:


let bearing = 0;if (this.currentPointIndex < this.trackPoints.length - 1) {  const currentPoint = this.trackPoints[this.currentPointIndex];  const nextPoint = this.trackPoints[this.currentPointIndex + 1];  bearing = Math.atan2(    nextPoint.lat - currentPoint.lat,    nextPoint.lng - currentPoint.lng  ) * 180 / Math.PI;  bearing = (bearing + 360) % 360;  bearing = (360 - bearing + 90) % 360;}
this.mapController?.mapStatus.setRotate(bearing).setOverlooking(90).setCenterPoint(new LatLng(this.trackPoints[this.currentPointIndex].lat, this.trackPoints[this.currentPointIndex].lng)).refresh();
复制代码


四、总结


通过上述步骤,我们成功实现了类似 Keep 的轨迹播放效果。不仅提升了用户体验,还为运动数据的可视化提供了有力支持。

发布于: 刚刚阅读数: 5
用户头像

还未添加个人签名 2021-11-19 加入

还未添加个人简介

评论

发布
暂无评论
鸿蒙运动开发实战:打造 Keep 式轨迹播放效果_鸿蒙_王二蛋和他的张大花_InfoQ写作社区