rAF实现表格内容自动滚动
source link: https://www.clzczh.top/2022/06/18/table-auto-scroll/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
rAF实现表格内容自动滚动
实习导师让我实现表格内容自动滚动,实际就是大屏项目可能会用上的。正好之前看到rAF
的介绍,实际上也没用过,所以就用rAF
来玩一波。
目标:
- 让表格的内容会自动滚动
- 鼠标悬浮,动画停止
- 到底后会自动回到顶部,继续滚动
element表格自带滚动
首先呢,element
的表格是自带了滚动效果的,但是需要小小的设置一下。
<template>
<div class="mytable">
<el-table :data="tableData" ref="mytable">
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
<el-table-column prop="address" label="Address" />
</el-table>
</div>
</template>
<script setup>
import { reactive } from "vue";
const tableData = reactive([
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
]);
</script>
<style lang="less" scoped>
.mytable {
width: 400px;
height: 420px;
}
</style>
可以发现,这个时候超过表格的部分直接溢出来了。
这个时候可能会想:直接使用overflow: auto;
不就能实现表格滚动了吗?但是,这个表格滚动效果并不是想要的表格内容滚动。表头也会滚不见。
然后,我们使用Devtools
工具看一下:
发现上面红框框的元素高是100%
,但是它们的父元素的高不是100%,所以外层的高并没有传过来。所以我们只需要把 el-scrollbar
的祖先元素的高设置为100%
即可。
又有新问题出现了:表格有一部分内容被切掉了。
这时候我们仔细想一下就会发现,上面的滚动的只是表格内容,但是实际上我们把高度慢慢地传下来了,所以这个时候的滚动盒子 el-scrollbar
的高度也是整个表格的高度。所以我们需要将它的高度减掉表头的高度。
.el-scrollbar {
height: calc(100% - 40px);
}
这样子,前置任务就初步完成了。
rAF介绍
rAF:**requestAnimationFrame
**,实际上就是一个函数,会告诉浏览器:希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
- 由系统来决定回调函数的执行时机。如果显示器刷新的频率是60Hz,那么回调韩式就是每
1/60
s,即16.7ms执行一次。也就是说rAF
会跟着显示器地刷新频率走,能保证回调函数在每一次的刷新间隔制备执行一次,这样就不会引起丢帧,动画更流畅。 - 窗口没激活时,动画将暂停以提升性能和电池寿命
实现自动滚动
获取el-scrollbar
容器
需要实现自动滚动,首先得获取el-scrollbar
容器。访问路径在mytable -> scrollBarRef -> wrap$
。
onMounted(() => {
const { proxy } = getCurrentInstance();
const mytable = proxy.$refs.mytable;
const el = mytable.scrollBarRef.wrap$;
console.log(el);
});
编写动画方法,并使用rAF添加回调
首先,需要编写我们的滚动动画方法,很简单,只需要让滚动容器的scrollTop
一直加就行了。但是,为了让这个动画不只是会执行一次,所以在最后还得使用rAF
添加回调。当然,在onMounted
钩子中也需要添加一次回调。
function myscroll(el) {
el.scrollTop += 2;
window.requestAnimationFrame(myscroll.bind(null, el)); // 该动画方法需要携带参数,所以使用bind方法来携带参数
}
鼠标悬浮,动画停止
我们上面已经初步让表格内容滚动起来了,接下来实现一下第二个步骤鼠标悬浮,动画停止
停止rAF动画,需要获取调用window.requestAnimationFrame()
方法时返回的 ID,和定时器方法一样,所以我们调用window.requestAnimationFrame()
方法时,需要使用一个变量接住它,方便停止的时候使用。
然后,绑定鼠标事件:mouseenter
:动画停止,mouseleave
:继续动画。
let rAFid;
onMounted(() => {
const { proxy } = getCurrentInstance();
const mytable = proxy.$refs.mytable;
const el = mytable.$refs.scrollBarRef.wrap$;
console.log(el);
rAFid = window.requestAnimationFrame(myscroll.bind(null, el));
el.addEventListener("mouseenter", () => {
window.cancelAnimationFrame(rAFid);
});
el.addEventListener("mouseleave", () => {
rAFid = window.requestAnimationFrame(myscroll.bind(null, el));
});
});
function myscroll(el) {
el.scrollTop += 2;
rAFid = window.requestAnimationFrame(myscroll.bind(null, el)); // 该动画方法需要携带参数,所以使用bind方法来携带参数
}
到底后会自动回到顶部,继续滚动
这里我们需要判断是否到底了,所以需要使用可视高度+距离顶部 >= 整个高度
的方式,即el.clientHeight + el.scrollTop >= el.scrollHeight
.
我们判断到底后,就使用原生js的scrollTo
方法,就能让它回到顶部。
function myscroll(el) {
el.scrollTop += 2;
if (el.clientHeight + el.scrollTop >= el.scrollHeight) {
// cancelAnimationFrame(rAFid);
el.scrollTo({
top: 0,
});
}
rAFid = requestAnimationFrame(myscroll.bind(null, el));
}
可以看到已经实现了,而且是无缝衔接。
<template>
<div class="mytable">
<el-table :data="tableData" ref="mytable">
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
<el-table-column prop="address" label="Address" />
</el-table>
</div>
</template>
<script setup>
import { reactive, getCurrentInstance, onMounted } from "vue";
const tableData = reactive([
{
date: "2016-05-03",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-01",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-02",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
{
date: "2016-05-04",
name: "Tom",
address: "No. 189, Grove St, Los Angeles",
},
]);
let rAFid;
onMounted(() => {
const { proxy } = getCurrentInstance();
const mytable = proxy.$refs.mytable;
const el = mytable.$refs.scrollBarRef.wrap$;
console.log(el);
rAFid = window.requestAnimationFrame(myscroll.bind(null, el));
el.addEventListener("mouseenter", () => {
window.cancelAnimationFrame(rAFid);
});
el.addEventListener("mouseleave", () => {
rAFid = window.requestAnimationFrame(myscroll.bind(null, el));
});
});
function myscroll(el) {
el.scrollTop += 2;
if (el.clientHeight + el.scrollTop >= el.scrollHeight) {
el.scrollTo({
top: 0,
});
}
rAFid = requestAnimationFrame(myscroll.bind(null, el));
}
</script>
<style lang="less" scoped>
.mytable {
width: 400px;
height: 420px;
:deep(.el-table) {
height: 100%;
.el-table__inner-wrapper {
height: 100%;
}
.el-table__body-wrapper {
height: 100%;
}
.el-scrollbar {
height: calc(100% - 40px);
}
}
}
</style>
上面已经能够实现表格内容自动滚动了,但是有时候需要突出排在前面的话,可能会到顶部需要慢慢地回滚到顶部,再重新自动滚动。也就是说,scrollTo
方法的参数添加 behavior: "smooth"
来让它圆滑的回滚到顶部。
但是,我们添加了这个选项后,反而不回滚了。这是因为动画一直都还在,回滚速度又不够动画的。所以我们回滚前还得把动画给停止掉。
function myscroll(el) {
el.scrollTop += 2;
if (el.clientHeight + el.scrollTop >= el.scrollHeight) {
window.cancelAnimationFrame(rAFid);
el.scrollTo({
top: 0,
behavior: "smooth",
});
} else {
rAFid = requestAnimationFrame(myscroll.bind(null, el));
}
}
回滚后,动画停了,所以我们还得添加一个定时器,回滚后停一会,再重新开始动画。
function myscroll(el) {
el.scrollTop += 2;
if (el.clientHeight + el.scrollTop >= el.scrollHeight) {
window.cancelAnimationFrame(rAFid);
el.scrollTo({
top: 0,
behavior: "smooth",
});
setTimeout(() => {
rAFid = requestAnimationFrame(myscroll.bind(null, el));
}, 1000);
} else {
rAFid = requestAnimationFrame(myscroll.bind(null, el));
}
}
最后,还得添加一个钩子,清除rAF
动画,避免内存泄漏等问题的发生。
onBeforeUnmount(() => {
window.cancelAnimationFrame(rAFid);
});
- requestAnimationFram优势
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK