开始前,单位需要提出类似抖音的要求。这应该是客户的任务。但是,我不知道该怎么做。但是仔细研究后发现网上并没有成熟的解决方案,但是有很多需求。各大论坛满是问题,却很少有人回答和解决。这一刻,我慌了。毕竟单位的工作和时间安排是固定的。这个时候,退兵是最坏的政策。所以我只能撸起袖子干活了。毕竟,我必须含着眼泪完成我所做的事情。这是男人,一口唾沫一钉!大家都知道web端和client端相比有几个缺点。如果你想创建类似于客户端的复杂交互效果,你需要考虑几个问题:性能——如何做到最好(当然你想和客户端看齐是不可能的)体验——如何实现一个优秀的客户端体验,比如视频缓冲如何处理,初始化如何处理,视频预加载是否兼容——iOS和Android设备和不同版本之间浏览器的实现略有不同,但经过我研究抖音的web端,git上的一些开源相关的项目,还有一些零散的回答,我发现没有一个是符合他们实现的,所以只能搜集了几百个族长自己来了。既然他一个人来了,我们就需要针对目前的三个问题,找到能够解决问题并快速实现的方案(毕竟是有时间表的)。在实现的最初构思中,我们不仅仅需要解决问题,其实你还需要考虑一些架构设计,就是你如何分离注意力,如何详细拆解组件的粒度,可以把每个组件分开,对外单独引用,如何让每个组件通用,方便以后维护,又可以快速开发,不耽误工期。这些其实都是你在这项工作之初需要思考的一些问题。总结如下。首先,毫无疑问,如果要实现滑动的效果,目前最快的解决方案就是swiper,而swiper在web端的地位也是不可动摇的。其次,大家都知道原生的video标签体验很乱,所以这部分需要自己实现,比如进度条,拖拽,暂停播放,缓冲等等。而类似于抖音中视频顶部的一些元素,比如点赞、分享等功能需要从外部导入,方便其他开发者在使用时自行定制。最后,如何拆分组件的结构,分别打包上传npm,供大家使用。组件设计的概念,我一知半解也能想到。接下来,我应该解决研究中发现的三个问题:最容易解决的问题是兼容性问题。Babel完美解决。cli工具的命令行是直接生成的。在实现功能的情况下,尽量使用旧版本。性能问题是最难解决的。如果渲染的视频很多,那么dom中必然会有很多视频。这里我们采用web抖音方案。整个dom只渲染一个activesidlevideo和其他slides中间渲染空节点,可以大大减少dom的数量,配合vue的diff提供不错的性能。体验问题虽然不难,但是单靠前端是解决不了的。需要多方合作。需要压缩视频大小、提供封面图、增加缓冲效果等,不同设备、不同系统、不同版本在视频中的表现不同。很大,这其实是一个技术无法解决的兼容性问题,所以我们只能从交互的角度来解决问题。工程建设为了装最新的vite,工程建设体验了一下,开发体验确实丝滑快速。由于vite天然支持库开发,所以只需要在vite.config.ts中添加构建内容即可。build:{lib:{entry:path.resolve(__dirname,'src/components/index.ts'),name:'videoSlide',fileName:(format)=>`index.${format}.js`},rollupOptions:{//确保将你不想打包到库中的依赖项外部化external:['vue'],output:{//在UMD构建模式中为这些外部化的依赖项提供一个全局变量globals:{vue:'Vue'}}}},由于该库可能被ts高手使用,需要安装vite-plugin-dts插件生成d.ts文件。代码实现由于视频内容的处理和轮播部分是两个独立的逻辑,所以代码拆分成两个组件video.vue和slide.vue。video实现的基本思路是重写原有video标签的默认ui,达到自定义的目的。样式将不再详细描述。主要原因是video提供的一些事件改写了video的默认行为。下面简单介绍一下关键功能。//vue//jssetup({autoplay}){//是否处于暂停状态constpaused=ref(true);//总视频时间constendTime=ref(second(0));//播放时间conststartTime=ref(second(0));//是否按下constisPress=ref(false);//缓冲进度constpercentageBuffer=ref(0);//播放进度constpercentage=ref(0);//保存计算出的播放时间constcalculationTime=ref(0);//获取视频实例constvideo=ref(null);//是否显示封面图constshowImg=ref(true);//是否在缓冲区中constloading=ref(false);//播放函数play(){video.value.play();保罗sed.value=false;}//暂停函数pause(){if(paused.value)return;video.value.pause();暂停。值=真;loading.value=false;}//获取缓冲进度functionprogress(){if(!video.value)return;percentageBuffer.value=Math.floor((video.value.buffered.length?video.value.buffered.end(video.value.buffered.length-1)/video.value.duration:0)*100);}//时间变化函数durationchange(){endTime.value=second(video.value.duration);console.log("时间变化触发器");}//第一帧Loadingtrigger,为了获取视频时长functionloadeddata(){console.log("firstframerenderingtrigger");showImg.value=false;自动播放&&播放();}//当播放准备开始时(之前暂停或由于缺少数据而暂停)被触发functionplaying(){console.log("endofbuffering");loading.value=false;}//缓冲时触发functionwaiting(){console.log("inbuffering");loading.value=true;}//时间变化触发函数ctiontimeupdate(){//如果按下不能进入进度,说明需要进行拖动if(isPress.value||!video.value)return;startTime.value=second(Math.floor(video.value.currentTime));percentage.value=Math.floor((video.value.currentTime/video.value.duration)*100);}//按下开始触发functiontouchstart(){isPress.value=true;}//松开按钮触发函数touchend(){isPress.value=false;video.value.currentTime=calculationTime.value;}//拖动时触发函数touchmove(e){constwidth=window.screen.width;consttx=e.clientX||e.changedTouches[0].clientX;如果(tx<0||tx>宽度){返回;}calculationTime.value=video.value.duration*(tx/width);startTime.value=second(Math.floor(calculationTime.value));percentage.value=Math.floor((tx/width)*100);}//点击进度条触发函数handleProgress(e){touchmove(e);触摸端();}//播放结束时触发函数ended(){play();}onMounted(()=>{});返回{视频、暂停、暂停、播放、进度、durationchange、loadeddata、endTime、startTime、播放、百分比、等待、timeupdate、percentageBuffer、touchstart、touchend、touchmove、isPress、结束、handleProgress、loading、showImg、};},需要注意的是,自定义内容需要交给用户自定义,都是通过slot传递给当前组件,这样可以方便的根据内容自定义样式slide.vueslide.vue是处理滑动内容的组件。包括常用的上拉刷新、预加载等内容。核心代码如下://vue=index-1&&activeIndex<=index+1">//jssetup({list},{emit}){constactiveIndex=ref(0);functiontransitionStart(swiper){//表示不滑动,不处理if(activeIndex.value===swiper.activeIndex){//表示是第一个轮播if(swiper.swipeDirection==="prev"&&swiper.activeIndex===0){//表示上拉刷新emit("refresh");}elseif(swiper.swipeDirection==="next"&&swiper.activeIndex===list.length-1){//滑动到底部emit("toBottom");}}else{activeIndex.value=swiper.activeIndex;//预加载视频,提前加载数据if(swiper.activeIndex===list.length-1){emit("load");}}}return{transitionStart,activeIndex,};},这里有两点需要注意:为了预加载数据,滑动到最后一帧的时候会请求数据,但是由于请求是异步的,如果滑动到最后一个视频快速滑动会触发滑动到的事件底端。这时候新的数据请求回来后,就不是底部了。这个时候就需要做出判断了。如果你在请求的时候是滑动到底部,出于性能的考虑不要处理你的逻辑,只渲染active、prev、next内容,其他空节点全部渲染。为了防止页面出现多个vidoe标签,prev和next只渲染默认的图片内容。组合使用组合使用其实很简单。//vue点赞