全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2022-11-15_大屏经典组件:“无限滚动” 从分析到开发

您的位置:首页 >> 新闻 >> 行业资讯

大屏经典组件:“无限滚动” 从分析到开发 本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究! ??阅读本文,你将理解大屏 “无限滚动组件” 的开发思路跟随作者,一步步完成一个高性能 “无限滚动组件” 的开发收获一份该实现的粗糙源码。一、无限滚动:事件/告警 的有力帮手1.1 为什么需要滚动列表大屏之所以 “炫酷” ,相比于UI同学出的效果图,它最大的优势就在于它能动。 哪怕平台可能没有接入websocket,甚至数据就是静态写死的,客户依然希望数据能在屏幕上 “动起来”。 这会给人一种“数据是实时的”的错觉。 这种错觉,或者说故意营造出来的错觉,就是领导们“讲故事”的素材之一。 尤其是当业务里涉及到 “事件/告警/威胁/监控” 等元素时,涉及到的数据量很大 —— 几百或几千条,此时,会自己滚动的列表就成了非常适合场景的组件形式: “我们相关部门单据的申请和审批情况也会实时推送到系统中,可以做到实时把控。” ——领导如此向上介绍。 虽然大家都明白,但是谁会整天没事盯着一个深色的大屏做监管呢?这么炫酷的大屏,电脑不卡吗? 正经人都是用 "白色底+蓝色按钮" 的后台管理系统进行业务操作的。 可是汇报的时候,小小的列表就是关于 “实时监管” 的一个有力佐证。 1.2 为什么还得是 “无限滚动”?但是普通列表有一些非常明显的弊端: 它有尽头它的滚动没有质感它的衔接动画有不连贯感不理解?那我们看张图: 你有没有发现,它存在以下问题? 滚动是平缓的,没有节奏感。(相比于上面一次滚一行,然后停止若干时长后,进行下一次滚动) 滚动到最后一行后,即使立刻滚动到顶部,依然会产生明显的“不连贯感”。 为了解决以上问题,于是有了一种更为优质的视觉体验组件,它具备以下特性: 它似乎没有尽头 (滚动时,第一条数据就贴合在最后一条数据的后面,依此类推)它的动画连贯又流畅它的滚动更有质感它就是无限滚动,一个常见又经典的大屏组件。 二、实现思路分析2.1 需求分析ok,明确了“无限滚动”的必要性,让我们看看,它应该具备哪些特性? 假设,你有一个长度为4的列表,长这样: 那么它应该具备以下特性: 每次花费N秒滚动一单元格长度 (从A的上侧滚动到B的上侧)每次滚动结束后停留M秒,方便参观者查看数据。当D完全出现在视窗中之后,紧接着出现的应该是 A,然后是B,以此类推。一个最简单的无限滚动组件,最少应该具备以上三个特性。 接下来,就是头脑风暴的时间了: 无限滚动的列表,究竟应该如何实现? 2.2 思路A:修改元素排序这是最直观的思路,我们只持有原列表本身,通过滚动到一定阶段,调整的顺序,来完成“无限”的效果。 但是很可惜,这个方案: 存在较大弊端。 比如,当视窗大小只略小于列表大小时,就会出现这种情况: 即:A元素,既要出现在顶端,但同时也要出现在尾端。 这样一来,单纯排序就无法完全满足诉求了。 2.3 思路B:不仅排序,还复制元素为了解决上面思路A存在的问题,我们可以考虑通过Node.cloneNode()方式拷贝一个元素,手动让页面上同时存在两个A元素,一头一尾,就能补全上面那个场景的问题了。 但是,很可惜,这一方法也存在问题: MDN云: 克隆一个元素节点会拷贝它所有的属性以及属性值,当然也就包括了属性上绑定的事件 (比如onclick="alert(1)"),但不会拷贝那些使用addEventListener()方法或者node.onclick = fn这种用 JavaScript 动态绑定的事件。 简单来说,事件丢了。 最核心你的一点在于,通过改变元素结构来实现无限滚动这种方式,和React、Vue等集成了虚拟DOM的框架搭配使用时,也会遇到各种各样的结构同步的问题,会急剧增加框架的复杂性。 那么,有没有更简单的方法呢? 2.4 方案C:双倍的快乐众所周知: 动画是欺骗眼睛的艺术。 在帧与帧之间,画面其实是割裂的,人眼所能感知的最短时间大概是30ms,也就是说,如果按30ms作为间隔改变画面的形态,人眼就会认为画面是连续的。 因此,很多你看到的效果,其实都是在欺骗你的眼睛。 比如,你用两个完全相同的列表,就可以实现肉眼意义上的无限滚动。 如上图。 思路其实是: 两个完全相同的列表垂直排列,从头开始向下滚动。当第一个列表的下端达到视窗的上端时(此时它已经不可见了),立刻让第一个列表滚动到上端与视窗的上端重合。重复第一步之所以,这个思路可行,有两个关键点: 第2步改变状态前后,组件的视窗内看到的内容是一样的。第2步改变状态时,因为第二步是在瞬间完成的,并没有滚动过程,因此用户不会感知到发生过状态改变。因此,用户就能一直感觉到:“这个列表在向下无限地滚动”。 相比于 “方案A” 和 “方案B”,此方案最大的优势就在于: 它首先不需要改变元素的顺序它也不需要去通过cloneNode复制单个元素借用props.children(react) 或者slot/slot* 2 (vue),你就能轻易获得两份具备事件绑定的元素,逻辑简单又粗暴,不用编写复杂的代码。 综上所述,就用最轻轻松松的一笔,毁掉你所有的问题,我都选C,我都选C! 三、核心编码实现Talk is cheap,show me yourmoneycode。 3.1 准备生产工具首先,因为本系列都基于vue3,因此,有一个可运作的vue@3.x环境是必要的,至于是webpack或是vite并不重要。 甚至可以是一个UI库脚手架。(文末提供的demo会是这种形式的。) { "dependencies":{ "gsap":"latest",//我最顺手的动画库,当然你也可以选tween.js或者纯手写。 "@vueuse/core":"latest",//vuer必备的hooks工具库 } } ok,需要依赖的外部包就这些,接下来让我们开始建造。 3.2 元素布局设计让我们思考组件的元素布局,在我的规划中,它大概长这样: 在类名设计上,我们采用业内组件开发最常用的BEM规范 (参考链接),由外到内,分别是: .seamless-scroll:组件最外层元素。 .seamless-scroll__wrapper:具备position: relative和宽高100%的元素,目的是充满父元素。 之所以采用这种冗余的布局方式,是为了满足更多场景的使用,比如.seamless-scroll的position不应该被限定,可以使用absolute、fixed、relative等各种奇奇怪怪的布局。而.seamless-scroll__wrapper可以保证自身永远是relative状态的。 .seamless-scroll__box: 高度不受限的控件,它会在.seamless-scroll__wrapper的怀抱中滚动。 .seamless-scroll__box-top和.seamless-scroll__box-bottom就是那两份一模一样的列表的容器,它们的高度来自于列表项的撑起。 3.3API设计由于本文主要以讲解为主,目标不是做一个 “可以应对各种场景的组件”,因此我们只解决单一场景,所以API的设计上追求极致的简单: constprops=defineProps({ /** *两次滑动之间的停顿时长 */ delay:{ type:Number, default:1 }, /** *滑动单位距离需要的时间 */ duration:{ type:Number, default:2 } }) 以及,提供了一个默认插槽。 slot/slot 在这个插槽中,使用者可以去放列表的元素,它们各有各的高度和样式,这不应该是我们无限滚动应该接管的内容去接管的内容, 所以通过插槽的形式暴露出去。 3.4DOM结构及关键CSS关于DOM结构,只需要按本文3.2、3.3两个小节设计的思路,对照以下这张图就可以轻松完成构建: template divclass="seamless-scroll" divref="wrapperRef"class="seamless-scroll__wrapper" divref="boxRef"class="seamless-scroll__box" divclass="seamless-scroll__box-top"ref="topRef" slot/slot /div divclass="seamless-scroll__box-bottom" slot/slot /div /div /div /div /template scriptsetup constwrapperRef=ref(null) constboxRef=ref(null) consttopRef=ref(null) /script stylelang="scss" .seamless-scroll{ &__wrapper{ width:100%; height:100%; position:relative; overflow:hidden;//我们希望wrapper滚动,但不希望他露出丑陋的滚动条 } &__box{ &-top, &-bottom{ overflow:hidden; } } } /style 另外有个小TIPS: 关于封装组件时,style标签要不要使用scoped,我的建议是“不要用scoped,要用BEM CSS命名规范”,这样的好处在于方便其他组建对其引用样式,进行样式覆盖时,不会陷入CSS权重竟态问题。(不得不说,vue scoped相关机制,在这方面比react css module更友好一点点) 3.5 让列表滚动起来如果你有过使用tweenjs或者gsap这类动画库,你就能够明白,它们所做的最终要的一件事,就叫做补间。 所谓补间的意思,就是: 你指定一个对象,从状态A,耗费固定时间,以特定的方式变化到状态B。而之后该对象在每一帧的表现,就不再需要由你关注,相关的工具会自动计算出每帧对象的中间状态,并完成显示。 理解了这一点,我们就能很好地想到,列表的平滑滚动,其实就是把上面漫画里的top改成scrollTop的过程。 MDN ScrollTop 相关文档在此 所以,我们让列表滚动的核心代码,如下: importgsapfrom'gsap' onMounted(()={ consttimeLine=gsap.timeline()//为了后续更复杂的时间线安排,我们引入了gsap的timeline timeLine.to(wrapperRef.value,{scrollTop:200,duration:props.duration},`+=${props.delay}`) }) 就可以初步达到如下效果: 3.6 让列表有质感地滚动所谓有质感的滚动,其实是指一行一行地滚动。 所以,每一次滚动之前,我们都需要获得列表的元素们,但我们是通过插槽形式插入的列表,应该怎么在vue3里获得这些元素呢? constnodeList=topRef.value.childNodes constnodeArr=Array.from(nodeList.values()).filter(t=t.nodeType===Node.ELEMENT_NODE) 之所以要经过一轮 filter,是要排除掉那些空格文本(它们的nodeType是Node.TEXT_NODE) 再通过维护一个scrollingElIndex变量作为下标,记录当前滚动元素的index,就能准确获得:“这一次,我应该滚多远”这一重要信息。代码如下: letscrollingElIndex=0; constcurrentScrollingEl=nodeArr[scrollingElIndex]; scrollingElIndex=(scrollingElIndex+1)%nodeArr.length;//取完记得让`scrollingElIndex`下标+1,但只能在元素个数之内循环 接下来,我们需要计算元素的高度,此时,我推荐使用getBoundingClientRect方法,它和clientHeight的最大区别在于:它包含border,这会大大降低我们计算每个子元素高度的复杂度。 代码如下: letrect=currentScrollingEl.getBoundingClientRect(); constelHeight=rect.height constoffsetTop=currentScrollingEl.offsetTop constscrollTarget=offsetTop+elHeight; 上面代码片段里获取到的scrollTarget就是此次滚动我们需要滚动到的scrollTop的值。 使用这个思路,就可以很容易得到如下效果: 3.6 让列表无限滚动为了让列表达成无限滚动,按照我们2.4 方案C:双倍的快乐这一节的思路分析,其实核心就在于: 当上半部分的列表滚动到最后一个元素后,需要立刻让其恢复到初始位置。 这里只需要判断元素下标是否为0即可,非常容易: if(scrollingElIndex===0){ gsap.to(wrapperRef.value,{ scrollTop:0,duration:0,onComplete:()={ genAnimates()//先滚动到顶端再思考下一步动画 } }) } 上面的动画看似流畅,但其中已经包含了一次偷梁换柱。在这个过程中,列表实际上就已经具备了无限滚动的能力。 四、一些补充能力当列表高度过小时,应避免滚动,这时候就不应该通过slot/slot再复制一份元素了。 代码略,可参考文末源码 当鼠标移动到列表上之后,停止滚动,移出去后接着滚动,这对gsap.timeline来说小菜一碟。 constonMouseOver=()={ timeLine.pause() } constonMouseOut=()={ timeLine.resume() } 给元素一个奇偶数的状态类名 之所以需要给这个,是为了后续进行样式覆写,完成斑马线等效果,因为当box-top和box-bottom这两个列表同时存在时,它们的子元素为奇数,以及子元素为偶数,所需要覆写样式的思路会出现偏差。 五、DEMO& 文档为了写这一专栏,本菜鸡专门起了一个 基于vitepress的vue3微型组件库,用来放置相关代码,以及相关文档。(特此感谢:dewfall123/ruabick提供的脚手架 ,看库名就知道是一个老DOTA2玩家了) 当你需要在项目中使用到类似效果时,除了使用对你而言几乎是完全黑盒的开源库之外,你还可以参考本文,自行造轮子,自行沉淀组件库,并收获一个可视化开发上的小经验。 文档地址:windstorm-ui SeamlessScroll组件 源码地址:https://github.com/zhangshichun/windstorm-ui 阅读原文

上一篇:2021-12-17_全球主要国家数字经济的深度比较研究(附报告下载)∣ 企鹅经济学 下一篇:2021-05-29_“抖屏”连斩IAI两项大奖 , 新大屏营销模式破圈蓄势

TAG标签:

15
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价