喜欢户外运动的朋友一般都应该使用过运动APP(keep, 咕咚,悦跑圈,国外的Strava等)的一项功能,就是运动轨迹视频分享,分享到朋友圈或是运动群的圈子里。笔者本身平常也是喜欢户外跑、骑行、爬山等户外运动,也跑过半马、全马,疫情原因之前报的杭州的全马也延期了好几次了。回归正题,本文笔者基于自己的思想实现运动轨迹回放的一套算法策略,实现本身是基于Mapbox地图的,但是其实可以套用在任何地图都可以实现,基本可以脱离地图SDK的API。Mapbox 10 版本之后的官方给出的Demo里已经有类似轨迹回放的Case了,但是深度地依赖地图SDK本身的API,倘若在高德上实现很难可以迁移的。

这里先看下gif动图的效果,这是我在奥森跑的10KM的一个轨迹:

整个的实现包含了轨迹的回放,视频的录制,然后视频的录制这块不再笔者这篇文章的介绍的范畴内。所以这里主要介绍轨迹的回放,这个回放过程其实也是包含了大概10多种动画在里面的,辅助信息距离的文字跳转动画;距离下面配速、运动时间等的flap in 及 out的动画;播放button,底部button的渐变Visibility; 地图的缩放以及视觉角度的变化等;以上的这些也不做讨论。主要介绍轨迹回放、整公里点的显示(起始、结束), 回放过程中窗口控制等,作为主要的讲解范畴。

首先介绍笔者最开始的一种实现,假如以上轨迹List 有一百个点,每相邻的两个点做Animation之后,在AnimationEnd的Listener里开起距离下一个点的Animation,直到所有点结束,这里有个问题每次的运动轨迹的点的数量不一样,所以开起Animation的次数也不一样,整个轨迹回放的时间等于所有的Animation执行的时间和,每次动画启动需要损耗20~30ms。倘若要分享到微信朋友圈,视频的时间是限制的,但之前的那种方式时间上显然不可控,每次动画启动的损耗累加导致视频播放不完。

紧接着换成AnimationSet, 将各个线段Animation的动画放入Set里,然后playSequentially执行,同样存在上面的问题。假如只执行一次动画,那么这次动画start的损耗在整个视频播放上时长上的占比就可以忽略不计了,那如何才能将整个List的回放在一个Animation下执行完呢?假如轨迹只是一个普通的 Path,那么我们就可以基于Path的 length一个属性动画了,当转化到地图运动轨迹上去时,又如何去实现呢?

基于Path Length的属性动画

  1. 计算List对应的Path
  2. 通过PathMeasure获取 Path 的 Length
  3. 对Path做 Length的属性动画

这里有两套Point体系,一个是View的Path对应的Points, 然后就是Map上的List对应的Points,运动轨迹原始数据是Map上的List 点,上面的第一步就是将Map上的Points 转成屏幕Pixel对应的点并生成Path; 第二部通过PathMeasure 计算Path的Length; 最后在Path Length上做属性动画,然而这里并非将属性动画中每次渐变的值(这里对应的是View的Point点)绘制成View对应的Path,而是将渐变中的点又通过Map的SDK转成地图Location点,绘制地图轨迹。这里一共做了两道转换,中间只是借助View的Path做了一个依仗Length属性做的一个动画。因为基本上每种地图SDK都有Pixel 跟Location Point点互相transform的API,所以这个可以直接迁移到其它地图上,例如高德地图等。

下面具体看下代码,先将Location 转成View的Point体系,这里保存了总的一个Path,以及List 中两两相邻点对应的分段Path的一个list.

  • 生成Path:

其中用到 Mapbox地图API Location 点转View的PointF 接口API toScreenLocation(LatLng latlng), 这里生成List, 然后计算得到Path.

  • 基于Length做属性动画:

首先创建属性动画的 Instance:

ValueAnimator.ofObject(new DstPathEvaluator(), 0, mPathMeasure.getLength());

将每次渐变的值经过 calculateAnimPathData(value) 计算后存入到 以下的四个变量中,这里除了Length的渐变值,还附带有角度的一个二元组值。

dstPathEndPoint[0] = 0;//x坐标
dstPathEndPoint[1] = 0;//y坐标
dstPathTan[0] = 0;//角度值
dstPathTan[1] = 0;//角度值

然后将dstPathEndPoint 的值转成Mapbox的 Location的 Latlng 经纬度点,

PointF lastPoint = new PointF(dstPathEndPoint[0], dstPathEndPoint[1]);
LatLng lastLatLng = mapboxMap.getProjection().fromScreenLocation(lastPoint);
Point point = Point.fromLngLat(lastLatLng.getLongitude(), lastLatLng.getLatitude());

过滤掉一些动画过程中可能产生的异常点,最后加入到Mapbox的轨迹绘制的Layer中形成轨迹的一个渐变:

Location curLocation = mLocationList.get(animIndex);
float degrees = MapBoxPathUtil.getRotate(curLocation, point);
if (animIndex < 5 || Math.abs(degrees - curRotate) < 5) {//排除异常点setMarkerRecord(point);
}

setMarkerRecord(point) 方法调用加入到 Map 轨迹的绘制Layer中

动画过程中,当加入到Path中的点超过一定占比时,做了一个窗口显示的动画,窗口List跟整个List的一个计算:

//这里可以取后半段的数据,滑动窗口,保持 moveCamera 的窗口值不变。
int moveSize = passedPointList.size();
List<LatLng> windowPassList = passedPointList.subList(moveSize - windowLength, moveSize);

接下来看整公里点的绘制,看之前先看下上面的calculateAnimPathData()方法的逻辑

如上,length为当前Path走过的距离,假设轨迹一共100点,当前走到 49 ~ 50 点之间,那么calculateLength就是0到50这个点的Path的长度,它是大于length的,offsetLength = calculateLength - length; 记录的是 当前点到50号点的一个长度offsetLength,animIndex值当前值对应50,recordPathList为一开始提到的跟计算总Path时一个分段Path的List, 获取到49 ~ 50 这个Path对应的一个model.

RecordPathBean recordPathBean = recordPathList.get(animIndex);

获得Path(49 ~ 50) 的长度减去 当前点到 50的Path(cur ~ 50)的到 Path(49 ~ cur) 的长度

float stopD = (float) (pathMeasure.getLength() - offsetLengthCur);

然后最终通过PathMeasure的 getPosTan 获得dstPathEndPoint以及dstPathTan数据。

pathMeasure.getSegment(0, stopD, dstPath, false);
mDstPathMeasure = new PathMeasure(dstPath, false);
//这里有个参数 tan
mDstPathMeasure.getPosTan(mDstPathMeasure.getLength(), dstPathEndPoint, dstPathTan);
  • 整公里点的绘制

原始数据中的List的Location中存储了一个字段kilometer, 当某个Location是整公里点时该字段就有对应的值,每次Path属性渐变时,上面的逻辑里记录了lastAnimIndex, animIndex。当 animIndex > lastAnimIndex时, 上面的calculateAnimPathData() 方法里分析animIndex有可能还没走到,所以在animIndex > lastAnimIndex时lastAnimIndex肯定走到了。

当lastAnimIndex对应的点是 整公里时,做一个响应的属性动画。

至此,运动轨迹回放的一个动画执行逻辑分析完了,如文章开始所说,整个过程中其实还包含了好多种其它的动画,处理它们播放的一个时序问题,如何编排实现等等也是一个难点。另外还就是轨迹播放时的一个Camera的一个视觉跟踪的效果没有实现,这个用地图本身的Camera 的API是一种实现,但是如何跟上面的这些结合到一块;然后就是自行通过计算角度偏移,累计到一定的旋转角度时,转移地图的指南针;以上是笔者想到的方案,以上有计算角度的,但需要找准那个累计的角度值,然后大量实际数据适配。

最后,有需要了解轨迹回放功能其它实现的,可留言或私信笔者进行一起探讨。

原文作者:cxy107750
本文转自 [https://juejin.cn/post/7183602475591548986]

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
二、源码解析合集

三、开源框架合集

欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

运动APP视频轨迹回放分享实现相关推荐

  1. 运动轨迹绘制页面的设计与实现:仿照运动APP的轨迹记录功能将HTML5地理定位技术用于移动设备,实现地图显示与用户打车从起点到终点的运动轨迹绘制效果。

    随着人们对健康意识的提高,各类运动软件也逐渐流行.由于手机方便携带,又自带GPS定位功能,因此APP成为用户的首选.例如咕咚.益动等.这些软件都具有类似的一个功能模块,就是在电子地图上跟踪记录用户跑步 ...

  2. Android百度地图实例详解之仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,轨迹回放,导航等)

    转载请标明地址:http://blog.csdn.net/gaolei1201/article/details/60876811 2016一路有你,2017一起奋斗! 最近共享单车很火,动辄几亿美刀, ...

  3. 20210323第一家量产国产化蓝牙AOA高精度定位基站生态合能培训会上海站现场直播下午内容视频录像回放-深圳核芯物联原厂工程师罗良技术分享

    20210323第一家量产国产化蓝牙AOA高精度定位基站生态合能培训会上海站现场直播下午内容视频录像回放-深圳核芯物联原厂工程师罗良技术分享 作为国内第1家正向自主研发量产2.4G AOA/蓝牙AOA ...

  4. Vue Baidu Map 实现Vue版本地图轨迹回放(App端)

    Vue Baidu Map 地图轨迹回放 Vue Baidu Map 简介 Vue Baidu Map 安装 1.NPM 2.CDN Vue Baidu Map 引用 BmlLushu 引用 BmlL ...

  5. 直播视频app源码的分享功能,是这样实现的

    直播视频app源码拥有大量的用户和直播内容,为了方便用户分享平台内容,直播视频app源码实现了分享到第三方的功能,下面就是直播视频app源码实现分享功能的代码. <!doctype html&g ...

  6. web技术分享| 【高德地图】实现自定义的轨迹回放

    实现(轨迹回放)方式有两种: 第一种是使用 JS API 和 AMap.PolyLine(折线)等图形配合实现. 第二种是使用 JS API 和 AMapUI 组件库 配合使用,利用 PathSimp ...

  7. 车辆轨迹回放中如何实现轨迹信息表格的自动滚动?

    TSINGSEE青犀视频目前正在研发基于车载视频监控的新功能,包括轨迹跟踪.轨迹回放等.轨迹跟踪适用于车载监控场景,基于车内的车载监控装置,可以实时记录车辆的位置.行驶轨迹等信息,并且在轨迹回放中,能 ...

  8. GPS/轨迹追踪、轨迹回放、围栏控制

    折腾一个多月终于弄完了这个项目,起初都未曾接触GPS/轨迹追踪.轨迹回放.圈划围栏...等一些在百度地图或者Googel地图操作的一些业务,后端的业务相对来说简单点 cas单点登录,mongdb灵活的 ...

  9. 微信小程序实现活动轨迹回放

    前言 本文介绍使用组件map和API的MapContext+wx.getLocation来实现活动轨迹回放. 最终效果: 实现过程 1. 文件index.wxml代码如下,这一块比较简单,可自行查看, ...

最新文章

  1. GnuPG如何安全地分发私钥(1)GnuPG的用法
  2. 创建总账科目类型会计凭证
  3. Apple 预计于内华达州雷诺市再盖一个数据中心
  4. C# 中的委托和事件[转]
  5. sklearn自学指南(part48)--截断奇异值分解和潜在语义分析
  6. 『号外』 排名进入3000,特致感谢!
  7. qq邮箱如何在win10邮箱连接到服务器,win10系统下邮箱怎么添加qq邮箱
  8. 接口设计考虑点及验证点
  9. 批量给hive的表加分区
  10. java输入、输出流的简单入门
  11. 微型计算机技术试题,《微型计算机技术》试题库
  12. H5页面内实现分享给微信好友功能
  13. 吊打付费神器,安利两款视频压缩工具,不改变任何画质且永久免费使用的神器!
  14. 网易有道java面试题,网易有道研发类笔试题
  15. 台式linux系统安装,LINUX安装方法
  16. 易捷行云大规模云数据中心小时级安装部署|轻运维之安装部署
  17. 生命主题dreamweaver作业静态HTML网页设计——卫生与健康 6页 带视频
  18. 位置在此计算机上运行程序灰色,Win10电脑中定位服务按钮灰色无法开启的2种解决方法...
  19. 农业遥感技术科研成果汇总
  20. 遥感图像处理:最小噪声分离变换(Minimum Noise Fraction Rotation,MNF Rotation)

热门文章

  1. 韦东山 IMX6ULL和正点原子_「正点原子Linux连载」第四十三章Linux设备树(一)
  2. UICollectionView详解和UITableView的区别
  3. LIFT: Learned Invariant Feature Transform详细笔记
  4. 浅谈毫米波技术与应用
  5. 【安卓安全】透明代理定向抓APP包
  6. 【风控体系】互联网反欺诈体系建设
  7. 软件测试工程师好就业吗?软件测试工程师发展空间怎么样?
  8. 慧荣量产工具4连发(SM3255AA,SM3255AB,SM3252,SM3211)--来自忆捷售后
  9. 【Web前端大作业】基于HTML+CSS+JavaScript制作西北大学新闻网站(7页)
  10. 如何从ZINC数据库下载小分子用于虚拟筛选