Advertisement

轨迹的平滑移动

阅读量:

本文旨在记录开发过程中遇到的问题及其解决思路。由于专业深度不够,在回顾时希望能保持学习状态并有所感悟。文章如有不足之处,请赐教并提出改进意见;对于文中某些方法学而言,则可能有更好的解决方案替代。恳请大家多多交流与探讨

在2021年9月底时存在一份视频轨迹的需求。具体来说,在播放视频的同时,在左上角会展示一个小地图,并且这个地图中的一个点会与视频中无人机的位置同步移动(类似于CS或CF这类游戏中的位置显示小地图)。此外,该视频是由无人机拍摄并录制而成

该地图基于openlayers框架受限于最大缩放级别限制需要较大的地图缩放级别不适用默认图层经过多次更换尝试最终采用了BingMaps作为替代方案

复制代码
 layers: [

    
     new TileLayer({
    
     source: new source.BingMaps({
    
         key:"xxxxxxxx",
    
         imagerySet: "AerialWithLabelsOnDemand",
    
         culture: "zh-cn",
    
     }),
    
     }),
    
 ],

接下来相对容易处理。由于后台接口能够提供无人机移动轨迹上的每隔一秒的位置信息,并记录了其位置坐标以及运动方向等关键数据。通过整合这些位置数据即可实现与视频同步。

我首先通过相关接口调用代码获取了所需数据,并在项目中采用了 videojs 插件进行开发。随后,在项目中使用 videojs 插件自带的 loadedmetadata 方法成功获取到了视频总时长,并将起始时刻设置为时间轴的第一个数据点。通常情况下,结束时刻与最后一个数据点的时间坐标一致。

通过timeupdate方法对视频进度的变化进行监听。一旦发生改变,则会将时间点移动至相应时长的位置。

当 开始时间+视频播放时间 = 某个点的时间,就将轨迹点设置到该点。

复制代码
 // 视频时间改变时

    
     ee.$on("timeupdate", (videoTime) => {
    
       if (this.flag) {
    
         this.videoDroneList.find((v) => {
    
           if (
    
             Math.floor(+new Date(v.drone_timestamp) / 1000) ==
    
             Math.floor(this.startDroneTime / 1000 + videoTime)
    
           ) {
    
             markerEl.style.transform = `rotate(${v.yaw}deg)`;
    
             marker.setPosition([v.longitude, v.latitude]);
    
             olViewer.animate({
    
               center: [v.longitude, v.latitude],
    
               duration: 300,
    
             });
    
           }
    
         });
    
       }
    
     });

这样就完成了第一版需求。

然而,在这种做法中存在不足之处:该元素会呈现跳跃状态,并每隔一秒跳跃一次显得不够流畅或连续。因此建议在后续部分尽可能采用能够实现平滑移动的方式。

随后我也查阅了官方文档发现如果有这样一种方法可以让点从一个位置平滑地移动到另一个位置那就采用这种方法就可以彻底解决问题

在官方文档中有一个实例(Marker Animation)采用了另一种方法。简单来说就是首先获取所有的点并绘制其运动轨迹,在为这些点赋予一定的速度值后使其按照指定的速度在轨迹上平稳运行当然也可以根据需要调整这些速度数值

在第二版中,我能够先绘制所有点的轨迹,并无需担心其准确性问题。但问题是速度会有所变化,在这种情况下解决方案并不困难。由于我已经获得了每个点的具体坐标值,在计算时我可以确定任意两个相邻点之间的间距是多少,并且因为时间间隔已经被设定为1秒,在后续计算中只需要将该间距转换为每秒钟的速度数值即可完成处理工作(特别地需要注意最后一个数据点)。

复制代码
 this.pointDistance = this.haversine(); //haversine为计算两坐标点之间距离的方法

    
 this.pointSpeed = (this.pointDistance / 1);

运行,哒咩。

视频正常播放时会同步移动;此外还有重播功能、手动调节进度条以及倍速播放等功能可能导致播放速度受阻;然而我的平滑移动技术运行稳定;这将导致系统出现运行异常;相比之下,在第一版本的设计中,默认跳动是基于时间轴进行操作[...]因此在遇到重播、拖动进度条或倍速播放等问题时,默认跳动会受到影响;相比之下,在第一版本的设计中,默认跳动是基于时间轴进行操作;因此在遇到重播、拖动进度条或倍速 playback等问题时,默认跳动会受到影响;这样做的好处是可以避免因这些操作而导致的时间与关键点之间的对应关系发生变化;

但是现在伴随着流畅运行过程的推进,在初步测试中我们发现了多个潜在漏洞。目前看来这一方案存在明显缺陷。

良久,未果。

不得不得而行的情况下,我别无选择,只能被迫一一解决这些问题。第一步便是手动调节播放进度,这一动作会引发两个相关事件的发生:视频暂停和重播。为了实现这一点,我必须依赖 videoPlay 监听机制,它会在视频启动时主动地响应着 videoPlay 的变化。这样做的好处是可以实时追踪当前的播放比例,从而准确地将轨迹起始点设置在相应的位置上。举个例子来说,当视频已播放至 30% 时,则轨迹便随之起始点转移至该位置。尽管这一方法存在些许误差率的问题,但目前我暂时想不到更好的解决方案了。

复制代码
 // 手动快进,则会从进度this.proportion开始移动

    
 if (!!this.proportion || this.proportion == 0) {
    
     this.distance = this.proportion; //distance为0~1之间的值,0为起点1为终点
    
     this.proportion = null;
    
 }

其次是我获取了视频的倍率,并将播放速度乘上这个倍率值;这样做不仅能够避免出现任何问题(但感觉这个方法还有待完善之处),同时也留下了悬念

复制代码
 //pointDistance为这2点之间的距离,videoTimeArr[1]为视频的倍数

    
 this.pointSpeed = (this.pointDistance / 1) * videoTimeArr[1];

最后遇到网络卡顿问题后, 我一直致力于寻找能够有效监测网络卡顿的方法, 但是一直尝试各种方案均难以取得理想效果. 最近使用百度编程软件后发现这种方法也不太管用, 这些方法效果都不是很好.

我利用定时器,在两个相邻时间段(这里设定为100ms)内对视频的播放进度进行比较分析。如果发现这两个时间段内的播放进度无明显变化,则表明当前视频处于暂停状态或存在卡顿现象,并将其当前播放速度设为零

复制代码
 // 卡顿速度为0

    
 let lastTime; //上一个时刻的时间
    
 ee.$on("timeupdate", (videoTimeArr) => {
    
     clearInterval(intervalId);
    
     intervalId = window.setInterval(() => {
    
     if (videoTimeArr[0] != lastTime) { //videoTimeArr[0]为视频时间
    
         lastTime = videoTimeArr[0];
    
     } else {
    
         this.pointSpeed = 0; //pointSpeed为点的速度
    
     }
    
     }, 100);
    
 })

另外还有一些小问题需要解决,并对整个系统进行了集成调试完成之后,运动轨迹最终实现了平稳且正常的运行状态。

...
英文原样保留

复制代码
 // 平滑移动的方法

    
 moveFeature(event) {
    
       const time = event.frameState.time; //当前的时间戳
    
       const elapsedTime = time - this.lastTime;
    
       this.distance = this.distance + (this.pointSpeed * elapsedTime) / 1e6;
    
       // 手动快进,则会从进度this.proportion开始移动
    
       if (!!this.proportion || this.proportion == 0) {
    
         this.distance = this.proportion;
    
         this.proportion = null;
    
       }
    
       this.lastTime = time;
    
       // this.distance是0-1之间的值
    
       const currentCoordinate = route.getCoordinateAt(
    
         this.distance > 1 ? 1 : this.distance
    
       );
    
       olViewer.animate({
    
         center: currentCoordinate,
    
         duration: 10,
    
       });
    
       position.setCoordinates(currentCoordinate);
    
       const vectorContext = getVectorContext(event);
    
       if (markerStyle) {
    
         vectorContext.setStyle(markerStyle);
    
       } else {
    
         vectorContext.setStyle(styles.geoMarker);
    
       }
    
       vectorContext.drawGeometry(position);
    
       map.render();
    
 ​
    
       // 停止播放
    
       ee.$off("videoPause");
    
       ee.$on("videoPause", (videoTime) => {
    
         this.iptNumFlag = true;
    
         this.stopAnimation();
    
       });
    
 },

运行,ya-da-zie!

gif图片

经过反复调试后,在初步观察中我发现当单击操作完成后很快就会停止。然而,在深入分析后发现随着观察的深入我发现当处理速度较慢时find函数所需的时间也会显著增加

但是又感觉这计算量也不多

待续... ...

全部评论 (0)

还没有任何评论哟~