AutoScroll 自动滚动组件文档
组件介绍
旨在为公告、新闻、实时数据、跑马灯等需要循环展示的场景提供优雅的解决方案。支持无缝衔接效果,确保内容在滚动时不会出现断层或闪烁。
INFO
通过灵活的配置项,可以轻松控制滚动速度、方向、延迟等参数,同时支持鼠标悬停暂停功能,提升用户体验。组件内置 Mac 风格滚动条,支持响应式布局,能够自动适应不同屏幕尺寸。无论是简单的文字公告还是复杂的动态内容,AutoScroll 都能以流畅的性能和简洁的 API 满足需求。此外,组件提供了完善的事件监听和状态管理机制,方便开发者实现更复杂的交互逻辑。
特性
- 支持自动滚动和手动滚动切换
- 支持上下滚动方向
- 无缝衔接滚动效果
- 支持鼠标悬停暂停
- 自定义滚动速度和延迟
- Mac 风格滚动条
- 响应式自适应
安装使用
js
import AutoScroll from './AutoScroll.vue'
export default {
components: { AutoScroll }
}
Props 配置项
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
speed | 滚动速度(px/s) | Number | 50 |
scrollable | 是否启用自动滚动 | Boolean | true |
hoverPause | 是否鼠标悬停暂停 | Boolean | true |
direction | 滚动方向,可选值:'up'/'down' | String | 'up' |
delay | 开始滚动的延迟时间(ms) | Number | 0 |
immediate | 是否立即开始滚动 | Boolean | true |
manualScroll | 当 scrollable 为 false 时是否允许手动滚动 | Boolean | true |
事件
事件名 | 说明 | 回调参数 |
---|---|---|
pause | 滚动暂停时触发 | - |
resume | 滚动恢复时触发 | - |
使用示例
1. 基础自动滚动
TIP
鼠标悬停暂停,鼠标离开恢复
loading
2. 自定义速度和方向
vue
<auto-scroll :speed="400" direction="up">
<!-- 内容 -->
</auto-scroll>
TIP
speed
和 direction
可以动态修改,案例中点击按钮改变滚动方向和速度
loading
3. 延迟开始滚动
vue
<auto-scroll :delay="1000" :immediate="false">
<!-- 内容 -->
</auto-scroll>
TIP
延迟滚动需要配合 immediate
使用,immediate
为 false 时,需要手动点击按钮开始滚动,案例中点击按钮 1 秒钟后开始滚动
loading
4. 禁用鼠标悬停暂停
vue
<auto-scroll :hover-pause="false">
<!-- 内容 -->
</auto-scroll>
TIP
hoverPause
为 false 时,鼠标悬停不会暂停滚动
loading
5. 手动滚动模式
vue
<auto-scroll :scrollable="false" :manual-scroll="true">
<!-- 内容 -->
</auto-scroll>
TIP
scrollable
为 false 时,自动滚动关闭,manualScroll
为 true 时,允许使用鼠标滚轮手动滚动
loading
组件代码
点击查看完整代码
vue
<template>
<div
class="scroll-container"
ref="container"
@mouseenter="hoverPause && pauseScroll()"
@mouseleave="hoverPause && resumeScroll()"
:class="{ 'allow-scroll': !scrollable && manualScroll }"
>
<div class="scroll-content" :style="contentStyle" ref="content" :class="{ 'manual-scroll': !scrollable && manualScroll }">
<div class="original-content" ref="original">
<slot></slot>
</div>
<div v-if="needClone" class="clone-content" :style="cloneStyle">
<slot></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'AutoScroll',
props: {
// 滚动速度(px/s)
speed: {
type: Number,
default: 50
},
// 是否启用自动滚动
scrollable: {
type: Boolean,
default: true
},
// 是否鼠标悬停暂停
hoverPause: {
type: Boolean,
default: true
},
// 滚动方向:up/down
direction: {
type: String,
default: 'up',
validator: value => ['up', 'down'].includes(value)
},
// 滚动延迟时间(ms)
delay: {
type: Number,
default: 0
},
// 是否立即开始滚动
immediate: {
type: Boolean,
default: true
},
// 是否允许手动滚动(当scrollable为false时生效)
manualScroll: {
type: Boolean,
default: true
}
},
data() {
return {
scrollTop: 0,
needClone: false,
contentHeight: 0,
containerHeight: 0,
animationFrame: null,
lastTimestamp: null,
isPaused: false
}
},
computed: {
contentStyle() {
const transform = `translateY(${this.direction === 'up' ? -this.scrollTop : this.scrollTop}px)`
return {
transform,
transition: this.isPaused ? 'transform 0.3s' : 'none'
}
},
cloneStyle() {
return {
position: 'absolute',
left: 0,
top: this.direction === 'up' ? '100%' : 0,
transform: this.direction === 'up' ? 'none' : 'translateY(-100%)'
}
}
},
mounted() {
if (this.immediate) {
this.init()
}
window.addEventListener('resize', this.init)
},
beforeDestroy() {
window.removeEventListener('resize', this.init)
this.stopScroll()
},
methods: {
init() {
this.$nextTick(() => {
this.containerHeight = this.$refs.container.offsetHeight
const originalContent = this.$refs.original
const contentHeight = originalContent ? originalContent.offsetHeight : 0
if (contentHeight > this.containerHeight) {
this.needClone = true
this.contentHeight = contentHeight
this.stopScroll()
if (this.delay > 0) {
setTimeout(() => this.startScroll(), this.delay)
} else {
this.startScroll()
}
} else {
this.needClone = false
this.stopScroll()
}
})
},
startScroll() {
if (!this.scrollable || this.isPaused) return
const animate = timestamp => {
if (!this.lastTimestamp) {
this.lastTimestamp = timestamp
}
const elapsed = timestamp - this.lastTimestamp
this.lastTimestamp = timestamp
const distance = (elapsed * this.speed) / 1000
this.scrollTop += distance
if (this.scrollTop >= this.contentHeight) {
this.scrollTop = 0
}
this.animationFrame = requestAnimationFrame(animate)
}
this.animationFrame = requestAnimationFrame(animate)
},
stopScroll() {
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame)
this.animationFrame = null
}
this.lastTimestamp = null
this.scrollTop = 0
},
pauseScroll() {
this.isPaused = true
if (this.animationFrame) {
cancelAnimationFrame(this.animationFrame)
this.animationFrame = null
}
this.lastTimestamp = null
this.$emit('pause')
},
resumeScroll() {
this.isPaused = false
this.startScroll()
this.$emit('resume')
},
// 手动重新开始滚动
restart() {
this.stopScroll()
this.init()
}
},
watch: {
immediate() {
this.init()
},
speed() {
this.restart()
},
direction() {
this.restart()
}
}
}
</script>
<style scoped>
.scroll-container {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.scroll-content {
will-change: transform;
position: relative;
}
.original-content,
.clone-content {
width: 100%;
}
/* 当允许手动滚动时,取消transform效果 */
.manual-scroll {
transform: none !important;
}
/* 手动滚动时隐藏克隆内容 */
.allow-scroll .clone-content {
display: none;
}
/* 允许手动滚动时的样式 */
.allow-scroll {
overflow-y: overlay;
}
/* 自定义滚动条样式 */
.allow-scroll::-webkit-scrollbar {
width: 8px;
background-color: transparent;
opacity: 0;
}
.allow-scroll:hover::-webkit-scrollbar {
opacity: 1;
}
.allow-scroll::-webkit-scrollbar-track {
background: transparent;
}
.allow-scroll::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 4px;
border: 2px solid transparent;
background-clip: content-box;
opacity: 0;
transition: opacity 0.3s;
}
.allow-scroll::-webkit-scrollbar-thumb:active,
.allow-scroll:hover::-webkit-scrollbar-thumb {
opacity: 1;
}
</style>
注意事项
- 容器必须设置固定高度
- 列表内容高度需要大于容器高度才会触发滚动
- 当
scrollable
为 false 时,manualScroll
控制是否允许手动滚动 - 滚动速度单位为 px/s,数值越大滚动越快
- 组件会自动监听容器大小变化并重新初始化
最佳实践
- 根据实际需求设置合适的滚动速度
- 建议开启
hoverPause
,提升用户体验 - 如需延迟滚动,可以配合
delay
和immediate
使用 - 手动滚动模式下建议保持默认的 Mac 风格滚动条样式