前言
在做数据大屏开发的过程中,经常出现需要对列表进行自动滚动的需求,为了个性化的实现各种需求,就封装了一个函数方法
解决方案
- 生成一个父元素,并给其固定的高度,通过$refs获取并赋值给
container
变量 - 获取一些关于
container
和其子元素的信息,如父容器高度(parentHeight
)、滚动高度(scrollHeight
)、第一个子元素的高度(childHeight
)、子元素数量(childCount
)、最大高度(maxHeight
)、当前滚动位置(toHeight
)和当前子元素索引(current
),并初始化一个名为timer
的计时器。 - 定义一个名为
scrollCore
的函数,该函数用于实现滚动效果。 - 在
scrollCore
函数中检查如果scrollHeight
大于parentHeight
,则创建一个定时器,以便每隔一段时间执行一次滚动操作。 - 在定时器中,
scrollCore
函数中逐渐增加容器的scrollTop
属性,从而实现滚动效果。
一、匀速自动滚动,并实现方法可对多个列表复用
思路:通过对ref的命名,来实现在方法中使用for循环来对多个列表生效
js
<template>
<div>
<ul class="shiJianGaoJingContent_right" ref="scroll0">
<li class="gaojing_info_list" v-for="(item, index) in scrollInfoList" :key="index">
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
scrollInfoList:[],
};
},
methods: {
// 列表自动滚动
scrollFn() {
let dataList = [];
for (let i = 0; i < 1; i++) {
let container = this.$refs["scroll" + i];
let parentHeight = container.offsetHeight || 0;
let scrollHeight = container.scrollHeight;
let childHeight = container.firstChild.offsetHeight;
let childCount = container.childNodes.length;
let maxHeight = childHeight * childCount;
let toHeight = container.scrollTop;
let current = 0;
dataList.push({
parentHeight,
scrollHeight,
childHeight,
childCount,
maxHeight,
toHeight,
current,
timer: null,
timerMouseWheel: null,
});
function scrollCore() {
{
if (scrollHeight > parentHeight) {
dataList[i].timer = setInterval(() => {
let scrollStep = 1; // 每帧滚动的步长,可以适当调整
if (container.scrollTop + parentHeight + 5 >= scrollHeight) {
// 如果滚动到底部,则将滚动位置重置为0
container.scrollTop = 0;
dataList[i].toHeight = 0;
dataList[i].current = 0;
} else {
if (container.scrollTop < dataList[i].toHeight + dataList[i].childHeight) {
// 如果尚未滚动到目标位置,则逐帧更新滚动位置
container.scrollTop += scrollStep;
} else {
// 已滚动到目标位置,更新当前子项索引和子项高度
dataList[i].current = (dataList[i].current + 1) % dataList[i].childCount;
dataList[i].childHeight = container.childNodes[dataList[i].current].offsetHeight;
dataList[i].toHeight = container.scrollTop;
}
}
}, 2000 / 60); // 设置每秒更新60次,可根据需求调整
}
}
}
scrollCore()
}
},
},
mounted() {},
};
</script>
注:该方法在自动滚动到底部后会返回到顶部,循环滚动需要更改逻辑,在下方有循环的方法。
二、如何暂时关闭自动滚动
当我们想自己通过鼠标滚轮滚动该列表时,使该列表的自动滚动暂停,我们可以添加了一个名为mousewheel
的事件监听器,以便在鼠标滚轮滚动时执行以下操作:
- 清除之前的定时器
dataList[i].timer
和dataList[i].timerMouseWheel
。 - 更新
dataList[i].toHeight
为当前滚动位置,并将滚动位置设置为相同的值。 - 设置一个新的定时器
dataList[i].timerMouseWheel
,在一段时间后重新调用scrollCore
函数,以恢复自动滚动效果。
ini
container.addEventListener("mousewheel", () => {
this.$nextTick(() => {
clearInterval(dataList[i].timer);
clearInterval(dataList[i].timerMouseWheel);
dataList[i].toHeight = container.scrollTop;
container.scrollTop = container.scrollTop;
dataList[i].timerMouseWheel = setTimeout(() => {
scrollCore()
}, 3000)
});
});
二、如何彻底关闭自动滚动
在上面的方法中会一直无限的滚动下去,那么我们如果想停下具体看某一项要把自动滚动关闭掉呢,我们可以通过将关闭方法写在container
的某个事件中,并将该事件派发给container
来实现
ini
// 监听关闭自动滚动的事件
container.addEventListener("closeScroll", () => {
this.$nextTick(() => {
clearInterval(dataList[i].timer);
clearInterval(dataList[i].timerMouseWheel);
toHeight = container.scrollTop;
container.scrollTop = container.scrollTop;
});
});
ini
// 完整代码
// 关闭列表自动滚动
closeListScroll(container) {
// 创建一个新的 自定义 事件
const closeScroll = new Event("closeScroll");
container.dispatchEvent(closeScroll)
},
// 列表自动滚动
scrollFn() {
let dataList = [];
for (let i = 0; i < 1; i++) {
let container = this.$refs["scroll" + i];
let parentHeight = container.offsetHeight || 0;
let scrollHeight = container.scrollHeight;
let childHeight = container.firstChild.offsetHeight;
let childCount = container.childNodes.length;
let maxHeight = childHeight * childCount;
let toHeight = container.scrollTop;
let current = 0;
dataList.push({
parentHeight,
scrollHeight,
childHeight,
childCount,
maxHeight,
toHeight,
current,
timer: null,
timerMouseWheel: null,
});
function scrollCore() {
{
if (scrollHeight > parentHeight) {
dataList[i].timer = setInterval(() => {
let scrollStep = 1; // 每帧滚动的步长,可以适当调整
if (container.scrollTop + parentHeight + 5 >= scrollHeight) {
// 如果滚动到底部,则将滚动位置重置为0
container.scrollTop = 0;
dataList[i].toHeight = 0;
dataList[i].current = 0;
} else {
if (container.scrollTop < dataList[i].toHeight + dataList[i].childHeight) {
// 如果尚未滚动到目标位置,则逐帧更新滚动位置
container.scrollTop += scrollStep;
} else {
// 已滚动到目标位置,更新当前子项索引和子项高度
dataList[i].current = (dataList[i].current + 1) % dataList[i].childCount;
dataList[i].childHeight = container.childNodes[dataList[i].current].offsetHeight;
dataList[i].toHeight = container.scrollTop;
}
}
}, 2000 / 60); // 设置每秒更新60次,可根据需求调整
}
}
}
scrollCore()
container.addEventListener("mousewheel", () => {
this.$nextTick(() => {
clearInterval(dataList[i].timer);
clearInterval(dataList[i].timerMouseWheel);
dataList[i].toHeight = container.scrollTop;
container.scrollTop = container.scrollTop;
dataList[i].timerMouseWheel = setTimeout(() => {
scrollCore()
}, 3000)
});
});
container.addEventListener("closeScroll", () => {
this.$nextTick(() => {
clearInterval(dataList[i].timer);
clearInterval(dataList[i].timerMouseWheel);
toHeight = container.scrollTop;
container.scrollTop = container.scrollTop;
});
});
}
},
通过如上代码 我们就可以通过调用closeListScroll()
方法来关闭列表自动滚动,如我们想要关闭ref=scroll0列表的自动滚动
javascript
// 示例 关闭ref=scroll0列表的自动滚动
// 某个方法中
clickSomeBtn(){
... //其他逻辑
this.closeListScroll(this.$refs["scroll0"])
}
三、如何使自动滚动无限循环,使其头尾相连
思路:将一份数据复制为两份,在滚动到第二份与第一份重合的时候 立刻将滚动高度归位,这样从视觉效果上来看,就是无限滚动的效果 要实现这个效果,首先是我们需要将一份数据变为两份,最为简单的实现思路为直接将数据变为两份
ruby
<li class="gaojing_info_list" v-for="(item, index) in [...scrollInfoList,...scrollInfoList]" :key="index">
但是这样的话,我们需要对所有的列表都进行更改,容易遗漏,不符合封装思路
于是我就想着通过DOM方法直接在封装函数中进行操作,实现思路为
- 使用DOM方法获取
container
的所有子元素,并将它们存储在名为children
的变量中。 - 创建一个文档片段(Document Fragment)并将子元素逐个克隆并添加到文档片段中。
- 一次性将文档片段添加回
container
中,以提高性能。 当我们实现了克隆两份数据后,通过对container.scrollTop >= scrollHeight / 2
的判断,来得到我们已经来到了第二页与初始位置重复的位置,在这个时候将滚动位置重置,在视觉上就会实现首尾相连无限滚动的效果
ini
// 列表自动循环滚动
scrollFn() {
let dataList = [];
for (let i = 0; i < 1; i++) {
let container = this.$refs["scroll" + i];
console.log(container);
// 使用 DOM 方法获取所有子元素
let children = container.children;
// 创建一个文档片段
let fragment = document.createDocumentFragment();
// 将子元素逐个重新插入到容器中
for (let ind = 0; ind < children.length; ind++) {
const child = children[ind].cloneNode(true);
console.log(child, "child");
fragment.appendChild(child);
}
// 一次性将文档片段添加回容器中
this.$refs["scroll" + i].appendChild(fragment);
let parentHeight = container.offsetHeight || 0;
let scrollHeight = container.scrollHeight;
let childHeight = container.firstChild.offsetHeight;
let childCount = container.childNodes.length;
let maxHeight = childHeight * childCount;
let toHeight = container.scrollTop;
let current = 0;
dataList.push({
parentHeight,
scrollHeight,
childHeight,
childCount,
maxHeight,
toHeight,
current,
timer: null,
});
function scrollCore() {
if (scrollHeight > parentHeight) {
dataList[i].timer = setInterval(() => {
let scrollStep = 1; // 每帧滚动的步长,可以适当调整
if (container.scrollTop >= scrollHeight / 2) {
// 滚动到与第一行重复的位置后 重置
container.scrollTop = 0;
dataList[i].toHeight = 0;
dataList[i].current = 0;
} else {
if (
container.scrollTop <
dataList[i].toHeight + dataList[i].childHeight
) {
// 如果尚未滚动到目标位置,则逐帧更新滚动位置
container.scrollTop += scrollStep;
} else {
// 已滚动到目标位置,更新当前子项索引和子项高度
dataList[i].current =
(dataList[i].current + 1) % dataList[i].childCount;
dataList[i].childHeight =
container.childNodes[dataList[i].current].offsetHeight;
dataList[i].toHeight = container.scrollTop;
}
}
}, 2000 / 60); // 设置每秒更新60次,可根据需求调整
}
}
scrollCore();
container.addEventListener("mousewheel", () => {
this.$nextTick(() => {
clearInterval(dataList[i].timer);
clearInterval(dataList[i].timerMouseWheel);
dataList[i].toHeight = container.scrollTop;
container.scrollTop = container.scrollTop;
dataList[i].timerMouseWheel = setTimeout(() => {
scrollCore();
}, 3000);
});
});
container.addEventListener("closeScroll", () => {
this.$nextTick(() => {
clearInterval(dataList[i].timer);
clearInterval(dataList[i].timerMouseWheel);
toHeight = container.scrollTop;
container.scrollTop = container.scrollTop;
});
});
}
}