接上一章的内容继续讲,本次的主题是用vue实现列表有间隔的平滑滚动。
那么按照惯例,我们还是先来分析问题。还是以向上滚动举例,每隔三秒列表滚动一行。先不考虑平滑的问题,如果只是让列表每隔三秒向上滚动一行,这其实很简单,各位也猜到了,按我的习惯,还是用定时器来解决。
设置定时器,每三秒,让列表向上移动一行的高度,然后当最后一条数据出现时,在下一个三秒后,恢复到初始状态。
下面是上面描述的代码实现:
tableTimerFun() {
var count = 0; //每滚动一次,count加1
//tableList是列表的数据对象,maxCanSee代表可视范围内的最大完整数据条数
if (this.tableList.length > this.maxCanSee) {
this.tableTimer = setInterval(() => {
//如果还没滚动到最后一条数据,则列表向上移动以上的高度
if (count < this.tableList.length - this.maxCanSee) {
this.tableTop -= this.tableLineHeight; //tableLineHeight代表列表中一行的高度
count++; //每滚动一次,count加1
} else { //如果滚动到最后一条,则恢复初始状态
count = 0;
this.tableTop = 0;
}
}, 3000);
}
},
上面的内容已经实现了列表间隔一段时间滚动,接下来就是解决平滑的问题。这次我们不像上一章一样让列表每隔0.1s向上滚动1个像素,用高频位移创造平滑移动的视觉效果。这次我们直接用css来处理。
css中有个非常好用的动态样式写法 transition,它可以让其渲染的对象在位置、尺寸等发生变化时,平滑的变化。所以我们直接让列表的容器获得以下样式:
transition: all 0.5s;
偷懒可以直接写all,如果你有特定的需求或者只让某种样式平滑变化,还是去看官方文档,这里我不多说明。0.5s就是变化的时间。
至此,用vue实现列表有间隔的平滑滚动其实已经实现了,但还有瑕疵,就是当最后一条数据出现,然后重置初始状态时,列表会快速滚动到列表头部。其实也不难理解,按上面的写法,正常情况都是向上移动一行的高度,而最后一条重置到初始状态时却移动了接近整个列表的高度。所以这个过程中会看到列表快速滚动。说是瑕疵但也不是瑕疵,看具体需求。有解决方案,这个放到下一篇文章讲。
剩下的其实就是做数据大屏的开发要考虑数据不足的情况,举个例子,可视范围内列表最多现实6条,但调接口只显示了3条,这时候列表其实时不需要滚动的,这个放到以后讲,但是下面的完整代码中有,有兴趣的可以提前了解一下。
下面是完整代码,还是那句话:下面的代码直接粘贴运行不会运行成功,因为下面的完整代码涉及接口调用,但所有功能已经一步到位,希望在看的你能通过注释更多地去理解,而不是简单地复制粘贴。希望能对你有所帮助。
<template>
<div class="orderProcess">
<div class="loading_div" v-show="!showFlag">
<!-- Loading样式自己写,不需要,直接删除即可 -->
<div>Loading...</div>
</div>
<div class="success_info_body" v-show="showFlag">
<div class="table_head">
<div class="tr1 tr">订单号</div>
<div class="tr2 tr">项目名称</div>
<div class="tr3 tr">需求方量</div>
<div class="tr4 tr">预交付日期</div>
<div class="tr5 tr">进度</div>
</div>
<div class="table_body">
<!-- tableTop随时间推移不对增减,即列表不断往上 -->
<div class="table_list" :style="{top: tableTop + 'px'}">
<div
class="tr_div"
v-for="(item,index) in tableList"
:key="index"
:class="{'exception_style_tr':item.overDays>6 && item.process < 100}"
>
<div
class="tr1 tr"
:class="{'exception_style':item.overDays>6 && item.process < 100, 'notice_style':item.overDays>0 && item.overDays<7 && item.process < 100}"
>{{item.orderNo}}</div>
<div
class="tr2 tr"
:class="{'exception_style':item.overDays>6 && item.process < 100, 'notice_style':item.overDays>0 && item.overDays<7 && item.process < 100}"
>{{item.projectName}}</div>
<div
class="tr3 tr"
:class="{'exception_style':item.overDays>6 && item.process < 100, 'notice_style':item.overDays>0 && item.overDays<7 && item.process < 100}"
v-if="item.needVol!='-'&&item.needVol!='无法计算'"
>{{Number(item.needVol).toFixed(3)}} m³</div>
<div
class="tr3 tr"
:class="{'exception_style':item.overDays>6 && item.process < 100, 'notice_style':item.overDays>0 && item.overDays<7 && item.process < 100}"
v-else
>-</div>
<div
class="tr4 tr"
:class="{'exception_style':item.overDays>6 && item.process < 100, 'notice_style':item.overDays>0 && item.overDays<7 && item.process < 100}"
>{{item.completeDate}}</div>
<div
class="tr5 tr"
:class="{'exception_style':item.overDays>6 && item.process < 100, 'notice_style':item.overDays>0 && item.overDays<7 && item.process < 100}"
v-if="item.process!='-'"
>{{Number(item.process).toFixed(2)}} %</div>
<div
class="tr5 tr"
:class="{'exception_style':item.overDays>6 && item.process < 100, 'notice_style':item.overDays>0 && item.overDays<7 && item.process < 100}"
v-else
>-</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
tableTimer: null,
tableTop: 0, //列表向上移动的像素
tableList: [], //tableList是列表的数据对象
showFlag: false,
componentTimer: null,
maxCanSee: 6, //maxCanSee代表可视范围内的最大完整数据条数
tableLineHeight: 45 //tableLineHeight代表列表中一行的高度
};
},
props: ["activeFactoryId"],
watch: {
activeFactoryId(val, oldVal) {
clearInterval(this.componentTimer);
this.bsGetOrderProcessList();
this.componentTimerFun();
}
},
beforeDestroy() {
clearInterval(this.componentTimer);
clearInterval(this.tableTimer);
},
mounted() {
},
methods: {
bsGetOrderProcessList() {
clearInterval(this.tableTimer);
this.tableTop = 0;
if (this.activeFactoryId != "") {
this.showFlag = false;
this.$ajax({
method: "get",
url: `` //接口地址,不公开
})
.then(res => {
this.tableList = res.data.data;
this.showFlag = true;
this.actionFun();
})
.catch(function(err) {
console.log("bsGetOrderProcessList error!");
});
}
},
actionFun() {
if (this.tableList.length > 6) {
this.tableTimerFun();
} else {
this.fillTableList();
}
this.showFlag = true;
},
fillTableList() {
var addLength = this.maxCanSee - this.tableList.length;
for (var i = 0; i < addLength; i++) {
this.tableList.push({
orderNo: "-",
projectName: "-",
needVol: "-",
completeDate: "-",
process: "-"
});
}
},
tableTimerFun() {
var count = 0; //每滚动一次,count加1
if (this.tableList.length > this.maxCanSee) { //tableList是列表的数据对象,maxCanSee代表可视范围内的最大完整数据条数
this.tableTimer = setInterval(() => {
if (count < this.tableList.length - this.maxCanSee) { //如果还没滚动到最后一条数据,则列表向上移动以上的高度
this.tableTop -= this.tableLineHeight; //tableLineHeight代表列表中一行的高度
count++; //每滚动一次,count加1
} else { //如果滚动到最后一条,则恢复初始状态
count = 0;
this.tableTop = 0;
}
}, 3000);
}
},
componentTimerFun() {
this.componentTimer = setInterval(() => {
this.bsGetOrderProcessList();
}, 3600000);
}
}
};
</script>
<style scoped>
.orderProcess {
width: 600px;
height: 313px;
}
.loading_div {
color: #eee;
padding-top: 100px;
}
.table_head {
width: 100%;
height: 30px;
line-height: 30px;
background: rgba(90, 127, 200, 0.5);
display: flex;
color: #eee;
text-align: center;
font-size: 15px;
}
.tr1 {
width: 25%;
}
.tr2 {
width: 25%;
}
.tr3 {
width: 18%;
}
.tr4 {
width: 18%;
}
.tr5 {
flex: 1;
}
.tr {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
box-sizing: border-box;
padding: 0 5px;
text-align: center;
font-size: 14px;
}
.table_body {
width: 100%;
height: 270px;
overflow: hidden;
position: relative;
}
.table_list {
width: 100%;
position: absolute;
transition: all 0.5s;
}
.tr_div {
width: 100%;
display: flex;
color: #eee;
text-align: center;
line-height: 45px;
font-size: 13px;
}
.exception_style_tr {
animation: exception_style_tr 0.8s linear;
-moz-animation: exception_style_tr 0.8s linear;
-webkit-animation: exception_style_tr 0.8s linear;
-o-animation: exception_style_tr 0.8s linear;
animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
}
@keyframes exception_style_tr {
0% {
background: rgba(3, 145, 167, 0.1);
}
50% {
background: rgba(250, 4, 4, 0.15);
}
100% {
background: rgba(3, 145, 167, 0.1);
}
}
@-moz-keyframes exception_style_tr {
0% {
background: rgba(3, 145, 167, 0.1);
}
50% {
background: rgba(250, 4, 4, 0.15);
}
100% {
background: rgba(3, 145, 167, 0.1);
}
}
@-webkit-keyframes exception_style_tr {
0% {
background: rgba(3, 145, 167, 0.1);
}
50% {
background: rgba(250, 4, 4, 0.15);
}
100% {
background: rgba(3, 145, 167, 0.1);
}
}
@-o-keyframes exception_style_tr {
0% {
background: rgba(3, 145, 167, 0.1);
}
50% {
background: rgba(250, 4, 4, 0.15);
}
100% {
background: rgba(3, 145, 167, 0.1);
}
}
.exception_style {
font-weight: bold;
animation: exception_style 0.8s linear;
-moz-animation: exception_style 0.8s linear;
-webkit-animation: exception_style 0.8s linear;
-o-animation: exception_style 0.8s linear;
animation-iteration-count: infinite;
-webkit-animation-iteration-count: infinite;
}
@keyframes exception_style {
0% {
color: #eee;
}
50% {
color: #fa0404;
}
100% {
color: #eee;
}
}
@-moz-keyframes exception_style {
0% {
color: #eee;
}
50% {
color: #fa0404;
}
100% {
color: #eee;
}
}
@-webkit-keyframes exception_style {
0% {
color: #eee;
}
50% {
color: #fa0404;
}
100% {
color: #eee;
}
}
@-o-keyframes exception_style {
0% {
color: #eee;
}
50% {
color: #fa0404;
}
100% {
color: #eee;
}
}
.notice_style {
font-weight: bold;
color: #d1ce02;
}
</style>
最后是效果视频: