OpenHarmony-ArkUI基于JSAPI的360°全景展示
时间:2023-03-14 20:56:29
科技观察
了解更多开源请访问:开源基础软件社区https://ost.51cto.com前言该功能已实现于之前的web,想直接搬过来修改,在OpenHarmony上也能跑。其实360度全景显示功能有很多用处。例如,一些购物平台用于全面展示商品,让您更全面、更直观地了解商品;还有一些销售平台可以展示一些全景。户型等等。项目说明工具版本:DevEcoStudio3.0Beta2。SDK版本:3.0.5.2(API版本7Beta2)。主要成分:画布。效果展示的实现原理是基于canvas,通过绘制360°的序列帧图来实现的。通过监听手势滑动实现图片框切换。实现过程1.创建canvas因为canvas是基于canvas实现的,首先当然要写一个canvas标签,添加两个手势事件touchstart和touchmove。
2.加载所有序列帧图片是通过image序列帧实现的,所以需要先预加载所有序列帧图片,才能进行后续操作,否则切换时会出现没有加载导致空白问题的图像。下面封装了一个imgLoad方法来处理所有的图片预加载。/***@param{Array}imgList需要预加载的图片数组*@param{Function}progressCb加载进度回调方法*@param{Function}completeCb所有加载完成回调*/constimgLoad=(imgList=[],progressCb,completeCb)=>{letlen=imgList.length;让数=0;让进度=0;varloadImage=function(src){returnnewPromise(function(resolve,reject){letimg=newImage();img.onload=function(){resolve(img);//加载时执行resolve函数};img.onerror=function(){reject('Addresserror:'+src);//抛出异常时执行reject函数};img.src=src;});};function*fn(){for(leti=0;i
{//Leaflet加载num++;progress=parseFloat((100/len)*num).toFixed(2);progressCb&&progressCb(img,progress);value=g.next().value;if(va卢){恢复();}else{//所有加载完成completeCb&&completeCb();}}).catch((err)=>{console.log(err)});}}上述方法有两个回调progressCb:单张加载完成的回调。该回调可以监听加载进度,获取创建的图片对象。completeCb:所有加载完成后回调。回调成功后,即可进行后续操作。这里介绍一个比较少见的函数,fn*:生成器函数(generatorfunction)。什么是生成器函数?生成器函数可以在执行期间暂停,然后从暂停处继续执行。可以使用yield关键字暂停函数。调用生成器函数会生成生成器的迭代器对象。使用next()方法。当第一次(后续)调用时,其中的语句将被执行,直到第一次(后续)yield发生,yield后面是迭代器要返回的值。yield*(多了一个星号),表示将执行权交给另一个生成器函数(当前生成器暂停执行)。next()->{value:value1,done:true|false}。value:表示本次返回的值,即yield表达式返回的值done:表示生成器后面是否有yield语句,即生成器函数是否已经执行并返回。注意:在next()方法中,如果传入了一个参数,那么这个参数就会作为上一行yield语句的返回值。3.调用图片预加载方法初始化canvas画布。创建一个序列帧图像路径数组。调用imgLoad方法。initCanvas(){让c=this.$refs.canvas;this.ctx=c.getContext('2d');让arr=[];for(vari=1;i<=this.imgLen;i++){arr.push(`common/images/car/${i}.png`)}imgLoad(arr,(img,progress)=>{lg.log('Progress:'+progress);this.imgList.push(img);},()=>{lg.log('allloaded')this.cutSpirit();})},4.监控gesturetouchStart方法监听触摸开始时的手势,缓存当前触摸位置的x轴数据作为起始数据。touchMove方法是监听滑动时的手势监听。获取当前滑动的x轴位置,并与缓存的startPoint进行比较。unit是滑动精度单位。目前的处理是将屏幕宽度除以2倍的序列帧长度360/(64*2),也就是说序列帧从0的x轴到最右边可以完成两次旋转屏幕。分子越大,滑动越快。每次滑动都需要缓存当前位置作为startPoint。从屏幕上看,从右向左滑动时,x值减小,所以定义type为right。从屏幕上看,从左向右滑动时,x值增加,所以定义type为left。注意:这里定义的类型不是sequenceframe需要走的类型,下一点会详细说明。touchStart(e){让s=e.touches[0].localX;this.startPoint=s;//lg.log('触摸开始:',s);},touchMove(e){让s=e。触摸[0].localX;if((s-this.startPoint)>this.unit){this.drawImg(this.imgIndex,'right')this.startPoint=s;lg.log('right:',s)}elseif((s-this.startPoint)<-this.unit){this.drawImg(this.imgIndex,'left')this.startPoint=s;lg.log('左:',s)}this.startPoint=s;},5.绘制序列帧图片手指滑动的方向不一定是序列帧图片滑动的方向。它还取决于序列帧渲染顺序是顺时针还是逆时针。如果sequenceframe是顺时针的,type是对的,sequenceframe是反方向的,所以sequenceframe需要逆时针绘制,所以this.imgIndex–;。如果sequenceframe是逆时针的,type是left和sequenceframe是同一个方向,所以sequenceframe是顺时针绘制的,所以this.imgIndex++,;。/***@param{Number}n当前绘制序列帧的下标*@param{String}type当前手指滑动方向*/drawImg(n,type){if(type=="right"){if(this.imgIndex>0){this.imgIndex--;}else{this.imgIndex=this.imgLen;}}elseif(type=="left"){if(this.imgIndex