一、需求
当某个程序在后端运行的时候,前端经常要显示一个动态的进度条,或者 loading 的动画,或者百分比的数字
当然,更好的效果是给当前的用户展示出程序运行的具体细节,比如运行中记录的日志,或者实时的进程环节
比如:
[12:01:59]正在启动应用进程[12:02:00]进程启动成功,正在获取网络资源...[12:02:01]成功启动下载任务[12:02:02]下载中....[12:02:03]下载成功,开始解析网络资源[12:02:04]正在安装相关程序[12:02:05]安装成功,应用进程结束
当然,这是我瞎写的,只是为了展示我们想要实现的日志的样子,当这样的日志内容或者进程内容很多的时候,最好的办法是随着内容的增加,让页面自动向下滚动,方便用户查看最新的消息
二、分析
在这个过程中,一方面要不断的调用接口来获取最新的数据,一方面还要把数据渲染到页面中,同时让页面发生滚动
注意这里要让页面产生滚动的动画,有三个条件
不能每次接口都返回全量数据直接替换页面数据,这样会导致页面没有滚动的动画
页面的 css 样式要正确设置,确保内容只在父元素的可视区域内发生滚动而不是整个页面滚动
使用正确的 API 来完成滚动的效果
三、实现方法
第一个条件是很好的做的,我们可以使用 setInterval
vartimer=setInertval(()=>{getLogList().then(res=>{randerView(res.data)})},2000)
不过这样做有个弊端,就是当用户网络不那么畅通,或者服务器比较拥挤的时候,已经调用的接口一直处于 pendding 的状态,后面的接口还在持续不断的调用,会让本就拥堵的服务雪上加霜
所以我更喜欢使用 setTimeout
asyncfunctiongetLogData(){constlogData=awaitgetLogList()randerView(logData.data)setTimeout(getLogData,2000)}getLogData()
第二个样式问题,只需要正确的给父元素添加固定的高度和overflow-y: scroll
就可以了。
下面来说说第三个问题,如何让内容自动的向下滚动,因为这部分是比较重点的内容,我就单独放一个小节里来说
页面自动滚动的方案探讨
一般来说,如何让内容自动的向下滚动有两种方法来实现。
scrollIntoView
scrollIntoView 方法会滚动元素的父容器,使调用 scrollIntoView 的元素在父元素中可见。
语法如下:
element.scrollIntoView(); // 等同于element.scrollIntoView(true) element.scrollIntoView(alignToTop); // Boolean型参数 element.scrollIntoView(scrollIntoViewOptions); // Object型参数
参数说明:
auto
、smooth
;block:定义垂直方向的对齐,可选start
, center
, end
, 或 nearest
;inline:定义水平方向的对齐,可选 start
, center
, end
, 或 nearest
使用方法:
<template><div><strong>进程日志</strong><divstyle="max-height:120px;position:relative"><divv-if="logs.length"><pv-for="(item,index)inlogs":key="index":id="(logs.length===index+1)?'scollLog':''">{{item}}</p></div><pv-else>暂无数据</p></div></div></template><script>import{Component,Vue,Watch,Prop}from'vue-property-decorator'import{formatTime}from'@/utils'@ComponentexportdefaultclassextendsVue{@Prop()privateLOGS:Array<object>;privatename:string='processLog';privatelogs:Array<string>=[];//getData将父组件传递过来的日志转成`[12:01:59]正在启动应用进程`这种格式privategetData(){this.logs=this.LOGS?this.LOGS.map((item:object):string=>'['+formatTime(item.updatedTime)+']'+item.content+'\n'):[]}@Watch('LOGS')scrollLogs(newValue){this.getData()//在日志渲染之后,将最底下的日志滚动到其父元素可视区域内this.$nextTick(()=>{if(newValue.length!==0){(document.getElementById('scollLog')asHTMLElement).scrollIntoView({behavior:'smooth',block:'nearest'})}})}mounted(){this.getData()}}</script><stylescoped>.logList-item{padding:8px0;margin:0;}</style>
总结
scrollIntoView 这个方法的对 ios safari 和 IE 不是很友好,其他的浏览器没有什么问题
另外,这个方法对布局也没有什么要求,简单方便又易于理解,只需要针对最后一条渲染的日志调用即可
scrollTo
这个方法是老生常谈了,这个方法可把内容滚动到指定的坐标。
语法如下:
scrollTo(xpos,ypos)
参数说明:
使用方法:
<template><div><divref="consoleWindow"><divid="console_output"ref="consoleOutput"><--这里写日志信息,注意,一定要在父元素外再套一层--></div></div></div></template><script>import{Component,Vue,Watch,Prop}from'vue-property-decorator'import{post}from'@/utils/http'@ComponentexportdefaultclassextendsVue{asyncgetData(){constres=awaitpost(`/api/get/log`,{read:true})this.formatData(res.data)}formatData(data){try{if(data.length){data.forEach(item=>{this.printLine('['+item.updateTime+']'+item.value)})}else{this.printLine('暂无数据','input_line')}}catch(e){console.log('error')}}printLine(s,type){if((s=String(s))){letn=document.createElement('pre')if(!type)type='output_line'if(n.innerText!==undefined){//IEhastouseinnerTextn.innerText=s}else{//FirefoxusescreateTextNoden.appendChild(document.createTextNode(s))}n.className=typethis.$refs.consoleOutput.appendChild(n)//添加完日志后,让父元素滚动到其自身的距离this.$refs.consoleWindow.scrollTop=this.$refs.consoleWindow.scrollHeight}}mounted(){this.getData()}}</script><stylescoped>.console-wraper{display:flex;flex-direction:column;height:100%;}.console_window{flex:1;background:#333;overflow:auto;.console_output{white-space:pre-wrap;word-wrap:break-word;padding:12px;font-weight:bold;}/deep/pre.input_line{font-size:14px;color:yellow;opacity:0.8;padding:020px;}/deep/pre.output_line{color:#fff;font-size:13px;white-space:pre-wrap;word-wrap:break-word;padding-left:40px;opacity:0.7;}}</style>
本文的内容到此就结束了,案例代码未经完整测试,有任何问题可以在评论区留言哦 :)
作者:晴天同学