当前位置: 首页 > 科技观察

太长了,巧妙优化了跑马灯_0

时间:2023-03-20 14:48:36 科技观察

前言我上周优化了一个跑马灯,因为跑马灯的长度太长,每条节点很多,所以即使最大条数限制在50条,还很长,你能看到下面如何优化它多久?看看之前的选框。优化前的写法前面的写法很简单。其实就是让那个很长的class="animate"的div在lottery-person-wrapper里面滚动。使用了css中的动画属性。用动画很好,但是控制不了跑马灯的长度,就是不想让50个item一起滚动,还不如让只需要出现在屏幕上的item滚动。于是我把scrolling改成item的绝对定位,然后用transform改变位置,再用transition实现动画的过渡。优化后的写法可以看到item节点没有那么多了。如何才能做到这一点?首先获取lottery-person-wrapper的宽度。this.animationWrapperWidth=this.$refs.animateWrapper.clientWidth;然后让一个项目出现在选取框内。mounted(){this.$nextTick(()=>{this.animationWrapperWidth=this.$refs.animateWrapper.clientWidth;this.emitItem();});}看看emit是怎么写的。首先你要知道:swiperUserList是从接口中获取的列表。swiperUserListShow是模板中遍历的列表。我们先取出swiperUserList中的第一个item,然后把这个item放到swiperUserList的末尾,这样swiperUserList一直保持50个item。然后,将这个项目的深拷贝放入swiperUserListShow。深拷贝的原因是不希望swiperUserListShow中的item与swiperUserList中的item有引用关系,否则会很混乱。遍历时,为每个项目添加一个id作为唯一键。接下来就是获取item的宽度clientWidth,然后在item末尾出现时计算endShowTime,在item完全消失时计算disappearTime。当item末尾出现时,将下一个item推入swiperUserListShow,使其出现在跑马灯中,当item完全消失时,将其从swiperUserListShow中移除。emitItem(){如果(!this.isShow){返回;}让swiperUser=this.swiperUserList.shift();this.swiperUserList.push(swiperUser);this.swiperUserListShow.push(Object.assign({},{...swiperUser,id:this.swiperId}));这个.swiperId+=1;this.$nextTick(()=>{letelm=this.$refs.swiperUserList[this.swiperUserListShow.length-1];letelmWidth=elm.clientWidth||0;letdisappearTime=(elmWidth+this.animationWrapperWidth)/60;让endShowTime=elmWidth/60;让moveItem=this.swiperUserListShow[this.swiperUserListShow.length-1];elm.style.transition=`transform${disappearTime}slinear`;elm.style.transform='translate(-100%,-50%)';//this.clearTimer(moveItem)moveItem.endShowTimer=window.setTimeout(()=>{clearTimeout(moveItem.endShowTimer);moveItem.endShowTimer=null;this.emitItem();},结束显示时间*1000);moveItem.disappearTimer=window.setTimeout(()=>{clearTimeout(moveItem.disappearTimer);moveItem.disappearTimer=null;this.swiperUserListShow.shift();},disappearTime*1000);});},基本上已经实现了,为什么叫basic呢?因为有两个坑。看坑。第一个是我们使用了setTimeout。当我们将页面切换到后台时,setTimeout中的代码被挂起,不会执行,但是页面上的动画会继续执行。elm.style.transition=`转换${disappearTime}s线性`;elm.style.transform='translate(-100%,-50%)';所以,为了解决这个bug,需要监听是否切出和切入后台,切换到后台时,清空所有setTimeouts和清空swiperUserListShow列表,切回页面,再次执行emitItem。mounted(){this.$nextTick(()=>{this.animationWrapperWidth=this.$refs.animateWrapper.clientWidth;this.emitItem();});//处理跑马灯退出前台后还在运行的问题,隐藏是直接清除显示列表document.addEventListener('visibilitychange',()=>{constisShow=document.visibilityState==='visible'this.handleSwiperListShow(isShow);});}methods:{//处理字幕显示列表并清除定时器handleSwiperListShow(isShow){if(isShow){this.emitItem();}else{this.swiperUserListShow.forEach((item)=>{clearTimeout(item.endShowTimer);clearTimeout(item.disappearTimer);});this.swiperUserListShow=[];}},}第二个陷阱是我们使用clientWidth来获取项目的宽度。当我们的页面中有标签,且跑马灯在某个标签下时,那么当前v-show激活了其他标签,会导致跑马灯被隐藏,无法获取到item的宽度。这个时候clientWidth的值为0,这样一来计算出来的endShowTime值为0,就会导致疯狂执行settimeout里面的内容。为了解决这个bug,需要在父组件中通过isShow来判断跑马灯页面是否隐藏。props:{isShow:{type:Boolean,default:false},}然后听isShow。watch:{//解决tab选项卡隐藏抽奖模块,无法获取itemclientWith的问题。隐藏就是直接清除显示列表isShow(newVal,oldVal){this.handleSwiperListShow(newVal)}},至此,优化过程就完美结束了。其实有一个比较简单的优化方法,但是并不适用于我的场景,不过我也分享一下。依旧是使用css的animation动画属性,然后使用animationEnd的监听事件。其他优化方案监听结束时,使用v-if销毁当前跑马灯,然后push两个item给swiperUserListShow,然后生成并显示跑马灯,实现animation动画。这是一个实现起来很方便的方案,但是由于只有我们同时推送的item个数,运行后需要显示后面两个,所以会有空格,会有一种不连续性,因此不使用此解决方案。