最近在项目中做到一个功能,官网的底部有一块区域,放了很多logo,需要实现的效果就是让这些logo横向无限循环滚动,下面直接讲一下我的思路和做法。 先看一下大概的一个效果,就是让这些小方块在这个区域横向滚动。
我这里以三行举列。 假设我们初始的小方块有19个,这个区域正好能放下18个,也就是多一个被遮住。 先看下代码
<!-- 横向无限滚动效果 -->
<template>
<div
class="container"
ref="container"
@mouseover="stopScroll"
@mouseleave="startScroll"
>
<div
class="list"
v-for="(item, i) in Math.ceil(arr.length / 3)"
:key="item"
>
<div class="item" v-for="v in arr.slice(i * 3, (i + 1) * 3)">
{{ v }}
</div>
</div>
<div
class="more"
style="width: 20px; height: 20px; background-color: pink"
></div>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], // 小方块列表
animateId: null, // 动画id
moreDom: null, // 交叉的DOM
ob: null, // 观察者
};
},
methods: {
addObserver() {
this.moreDom = document.querySelector(".more");
this.ob = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// moreDom 和 container 交叉 就复制一遍列表 保证无限滚动
this.arr = [...this.arr, ...this.arr];
}
});
},
{
root: document.querySelector(".container"),
rootMargin: "0px 180px 0px 0px",
}
);
this.ob.observe(this.moreDom);
},
startScroll() {
const dom = document.querySelector(".container");
dom.scrollLeft += 2; // 这个值越大,滚动越快
this.animateId = requestAnimationFrame(this.startScroll);
},
stopScroll() {
cancelAnimationFrame(this.animateId);
},
},
mounted() {
// 开始动画
this.startScroll();
// 添加观察者
this.addObserver();
},
beforeDestroy() {
// 组件卸载时,取消动画
cancelAnimationFrame(this.animateId);
// 取消观察
this.ob.unobserve(this.moreDom);
},
};
</script>
<style scoped>
.container {
width: 1200px;
height: 380px;
margin: 200px auto;
border: 1px solid;
box-sizing: border-box;
display: flex;
gap: 20px;
overflow: auto;
padding: 20px;
}
.container::-webkit-scrollbar {
display: none !important;
}
.list {
box-sizing: border-box;
flex-shrink: 0;
width: 180px;
display: flex;
flex-direction: column;
gap: 20px;
}
.item {
width: 180px;
height: 100px;
background-color: aquamarine;
align-self: flex-start;
}
</style>
首先说下布局,因为我是三行,所以列的数量就取决于arr的长度,通过计算 Math.ceil(arr.length / 3)算出初始有多少列,不一定整除,所以这里向上取整,arr.slice(i * 3, (i + 1) * 3)表示每一列的三个元素是哪三个,比如:第一列是123,第二列就是456,依次排列。.more这个元素就是被观察的对象,当more和container发生交叉就会触发回调,这里交叉简单理解是就是more元素出现在了container容器中,然后可以去做一些相应的逻辑,这里就是去复制一遍列表。IntersectionObserver就是观察者API,其中第一个参数是一个回调函数,第二个参数就是配置项
root: document.querySelector(".container"),rootMargin: "0px 180px 0px 0px",root表示交叉的容器,rootMargin这四个值分别表示与上右下左的margin值,简单理解就是css中就margin属性,这里也就是.more元素距离container容器的距离,设置这几个值必须要带上单位,0要写成0px(这里需要注意),为什么我这里要写180px,因为我不想.more元素完全出现在.container容器才去复制一遍列表而是提前先复制一遍列表(简单理解也就是预先加载好),这样就没有视察感,提前加载好数据,就不会有空缺的情况出现(这个值越大就越提前加载,同理如果你的列表是向上滚动,那你就设置第三个值(也就是下),最后在滚动方法startScroll中去重复执行requestAnimationFrame(this.startScroll)即可,为什么不使用定时器,因为定时器其实是不准的,而requestAnimationFrame是很精确的,效果上也很丝滑,不会有卡顿的感觉。
今天分享就到这,希望对您有帮助,感谢您的观看。