封装一个 autoScroll.js组件
<template>
<div class="scroll-outer" ref="outer" @mouseover="onMouseover" @mouseleave="onMouseleave">
<div class="scroll-inner-box" ref="scrollBox">
<div class="scroll-item-box" ref="scrollItemBox">
<slot></slot>
</div>
<div v-if="showSecond" class="scroll-item-box">
<slot></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'my-auto-scroll',
props: {
list: {
type: Array,
default: () => [
{ name: '张三1' },
{ name: '张三2' },
]
},
},
data() {
return {
y: 0,
speed: 0.4,
showSecond: false,
};
},
watch: {
list: {
handler(newVal) {
var that = this;
this.$nextTick(() => {
if (newVal && newVal.length > 0) {
let scrollBox = that.$refs.scrollBox;
let outer = that.$refs.outer;
if (this.myReq) {
cancelAnimationFrame(this.myReq);
}
// 开启动画
if (this.canRun()) this.reqAnimationFrame();
// 手动滚动到底部时滚动条重置到最上边,同时滚动盒子重置为top:0
outer.addEventListener('scroll', function () {
if (
outer.scrollTop + outer.clientHeight + 4 >=
outer.scrollHeight
) {
outer.scrollTop = 0;
that.y = 0;
scrollBox.style.top = 0;
}
});
}
});
},
deep: true,
immediate: true
}
},
methods: {
// 鼠标移入 停止滚动
onMouseover() {
clearTimeout(this.timer);
cancelAnimationFrame(this.myReq);
},
// 鼠标移出 继续滚动
onMouseleave() {
if (this.canRun()) this.reqAnimationFrame();
},
canRun() {
let scrollItemBox = this.$refs.scrollItemBox;
let scrollBox = this.$refs.scrollBox;
let outer = this.$refs.outer;
// 开启动画条件:滚动盒子(scrollBox)高度高于外层容器(outer)高度
if (outer.offsetHeight >= scrollItemBox.offsetHeight) {
this.showSecond = false;
outer.scrollTop = 0;
this.y = 0;
scrollBox.style.top = 0;
return false;
} else {
this.showSecond = true;
return true;
}
},
//获取dom元素的高度:content+padding+margin+border
getComputedHeight(dom) {
let computedStyle = getComputedStyle(dom);
let computedHeight =
dom.offsetHeight +
parseFloat(computedStyle.marginTop) +
parseFloat(computedStyle.marginBottom);
return computedHeight;
},
// reqAnimationFrame() {
// //外层容器
// // let outer = this.$refs.outer;
// //滚动盒子
// let scrollBox = this.$refs.scrollBox;
// //滚动盒子下边的第一个scroll-item-box,
// let scrollItemBox = this.$refs.scrollItemBox;
// //滚动速度
// // this.speed = this.speed > 1 ? 1 : this.speed < 0 ? 0.1 : this.speed;
// //取第一个scrollItemBox高度
// let definedHeight = this.getComputedHeight(scrollItemBox);
// //持续滚动
// this.y = this.y + this.speed;
// scrollBox.style.top = -this.y + 'px';
// //当滚动到第一个scroll-item-box高度时scrollBox重置为top:0,视觉上是无缝滚动
// if (this.y >= definedHeight) {
// this.y = 0;
// }
// this.myReq = window.requestAnimationFrame(this.reqAnimationFrame);
// }
//
//此处对滚动方法做了优化---上面的方法 滚动的时候会一帧一帧的 不够流畅
//在实现滚动效果时,使用 CSS 的 transform: translateY() 属性通常比直接修改元素的 top 属性具有更好的性能。这是因为修改 transform 不会引起浏览器的重布局(reflow)和重绘(repaint),从而减少了性能开销。
reqAnimationFrame() {
let scrollBox = this.$refs.scrollBox;
let scrollItemBox = this.$refs.scrollItemBox;
// 取第一个scrollItemBox高度
let definedHeight = this.getComputedHeight(scrollItemBox);
// 持续滚动
this.y += this.speed;
// 使用 translate 替代 top 来改变位置
scrollBox.style.transform = `translateY(-${this.y}px)`;
// 当滚动到第一个scroll-item-box高度时scrollBox重置,视觉上是无缝滚动
if (this.y >= definedHeight) {
this.y = 0; // 重置滚动距离
scrollBox.style.transform = 'translateY(0)'; // 重置位置
}
this.myReq = window.requestAnimationFrame(this.reqAnimationFrame.bind(this));
},
},
destroyed() {
cancelAnimationFrame(this.myReq);
if (this.timer) clearTimeout(this.timer);
}
};
</script>
<style lang="scss" scoped>
.scroll-outer {
height: 100%;
overflow-x: hidden;
position: relative;
&::-webkit-scrollbar {
width: 0.3vw;
}
&:hover::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 0.1vw rgba(0, 0, 0, 0.3);
border-radius: 0.1vw;
background-color: #295099;
opacity: 1;
// display: none;
}
&:hover::-webkit-scrollbar-thumb {
opacity: 1;
border-radius: 0.1vw;
-webkit-box-shadow: inset 0 0 0.1vw rgba(0, 0, 0, 0.3);
background-color: #0ba9ea;
}
}
.scroll-inner-box {
height: auto;
position: absolute;
width: 100%;
top: 0;
left: 0;
}
</style>
组件使用方法
<autoScroll :list="centerList">
<div v-for="(item, index) in centerList" :key="index">
{{item}}
</div>
</autoScroll>