🔥🔥高效易用的 Vue3 公告滚动组件:打造丝滑的内容滚动体验(附源码)
2025年12月25日 16:38
在各类后台管理系统、营销页面或信息展示场景中,公告滚动是高频且基础的交互需求 —— 既要实现内容自动向上滚动的展示效果,也要兼顾用户手动操作的灵活性。基于 Vue3 Setup 语法糖封装的这款公告滚动组件,完美平衡了「自动展示」与「手动交互」的需求,为前端开发提供了开箱即用、高度可定制的解决方案。
![]()
核心特性:兼顾体验与灵活性
这款组件围绕 “用户体验优先” 设计,核心功能覆盖场景全、交互细节拉满:
-
丝滑自动滚动:支持像素级缓慢向上滚动,滚动条与内容同步移动,滚到底部后无缝重置至顶部,避免内容断层;可通过
autoScrollSpeed参数自定义滚动速度(默认 1px / 帧),兼顾展示效率与视觉舒适度。 -
灵活的手动交互:
- 鼠标悬浮即时暂停滚动,移出后立即恢复,方便用户聚焦查看单条公告;
- 滚轮操作大幅提速(默认单次滚动 40px),可通过
wheelStep参数调整灵敏度,满足快速浏览需求; - 保留原生滚动条并支持自定义样式,手动拖拽滚动条后 1 秒自动恢复滚动,兼顾不同操作习惯。
- 响应式与兼容性:监听公告列表数据变化,数据更新后自动重新计算高度并重启滚动;兼容 Chrome、Firefox、Edge 等现代浏览器,适配不同内核的滚动条样式。
- 轻量且易扩展:无第三方依赖,基于 Vue3 原生 API 开发;组件样式、容器宽高可通过外部样式灵活覆盖,无需修改源码即可适配不同 UI 风格。
快速上手:极简集成,开箱即用
组件采用 Vue3 Setup 语法糖开发,集成流程极简:
-
引入组件:将
NoticeScroll.vue文件放入项目组件目录,在需要使用的页面直接导入; -
传入数据:仅需传递核心参数
list(公告列表数组),即可启动基础滚动功能; -
定制化配置(可选):通过
autoScrollSpeed(滚动速度)、wheelStep(滚轮步长)、pauseOnHover(悬浮暂停)等参数,快速适配业务场景。
示例代码简洁直观:
vue
<NoticeScroll
:list="noticeList"
:autoScrollSpeed="1"
:wheelStep="40"
style="width: 500px; height: 200px;"
/>
适用场景:覆盖多类信息展示需求
无论是后台系统的系统公告、电商页面的营销通知,还是资讯类产品的动态资讯,这款组件都能适配 —— 既满足 “无人操作时自动轮播展示” 的基础需求,也解决了 “用户想手动快速浏览 / 聚焦查看” 的交互痛点。组件内置的内存泄漏防护(卸载时清理定时器、事件监听),也保证了在复杂页面中使用的稳定性。
代码如下:
<template>
<div
class="notice-scroll-wrapper"
ref="wrapperRef"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
@wheel="handleWheel"
>
<ul class="notice-scroll-list">
<li
class="notice-scroll-item"
v-for="(item, index) in list"
:key="index"
>
{{ item }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
// 定义组件属性
const props = defineProps({
// 公告列表数据
list: {
type: Array,
required: true,
default: () => []
},
// 自动滚动速度(像素/帧)
autoScrollSpeed: {
type: Number,
default: 1
},
// 滚轮单次滚动步长(像素)
wheelStep: {
type: Number,
default: 40
},
// 帧率(建议60)
frameRate: {
type: Number,
default: 60
},
// 鼠标悬浮是否暂停
pauseOnHover: {
type: Boolean,
default: true
}
});
// DOM 引用
const wrapperRef = ref(null);
// 状态变量
const timer = ref(null); // 自动滚动定时器
const autoScrollTimer = ref(null); // 滚轮/滚动条后恢复定时器
const isHover = ref(false); // 是否悬浮
const wrapperH = ref(0); // 容器高度
const contentH = ref(0); // 内容总高度
const maxScrollTop = ref(0); // 滚动条最大位置
// 初始化
const init = () => {
if (!wrapperRef.value) return;
// 计算容器/内容高度
wrapperH.value = wrapperRef.value.offsetHeight;
contentH.value = wrapperRef.value.querySelector('.notice-scroll-list').offsetHeight;
maxScrollTop.value = contentH.value - wrapperH.value;
// 启动自动滚动
startAutoScroll();
};
// 启动自动滚动
const startAutoScroll = () => {
// 内容高度 ≤ 容器高度时,不滚动
if (contentH.value <= wrapperH.value) return;
// 清除旧定时器
clearInterval(timer.value);
// 逐帧更新滚动条位置
timer.value = setInterval(() => {
let current = wrapperRef.value.scrollTop;
current += props.autoScrollSpeed;
// 无缝重置:滚到底部回到顶部
if (current >= maxScrollTop.value) {
current = 0;
}
wrapperRef.value.scrollTop = current;
}, 1000 / props.frameRate);
};
// 停止自动滚动
const stopAutoScroll = () => {
clearInterval(timer.value);
timer.value = null;
};
// 鼠标移入:暂停滚动
const handleMouseEnter = () => {
if (!props.pauseOnHover) return;
isHover.value = true;
stopAutoScroll();
};
// 鼠标移出:恢复滚动
const handleMouseLeave = () => {
if (!props.pauseOnHover) return;
isHover.value = false;
startAutoScroll();
};
// 滚轮控制:提速滚动
const handleWheel = (e) => {
e.preventDefault();
// 暂停自动滚动
stopAutoScroll();
// 计算新滚动位置
const newScrollTop = wrapperRef.value.scrollTop + (e.deltaY > 0 ? props.wheelStep : -props.wheelStep);
// 边界限制
wrapperRef.value.scrollTop = Math.max(0, Math.min(newScrollTop, maxScrollTop.value));
// 1秒后恢复自动滚动
clearTimeout(autoScrollTimer.value);
autoScrollTimer.value = setTimeout(() => {
if (!isHover.value) startAutoScroll();
}, 1000);
};
// 监听滚动条手动拖动:恢复自动滚动
const handleScroll = () => {
// 排除自动滚动触发的scroll事件
if (timer.value) return;
stopAutoScroll();
clearTimeout(autoScrollTimer.value);
autoScrollTimer.value = setTimeout(() => {
if (!isHover.value) startAutoScroll();
}, 1000);
};
// 监听列表数据变化:重新初始化
watch(
() => props.list,
() => {
// 清除旧定时器
clearInterval(timer.value);
clearTimeout(autoScrollTimer.value);
// 重新计算高度并启动
setTimeout(init, 0); // 异步等待DOM更新
},
{ deep: true }
);
// 生命周期:挂载时初始化
onMounted(() => {
init();
// 绑定滚动条拖动事件
if (wrapperRef.value) {
wrapperRef.value.addEventListener('scroll', handleScroll);
}
});
// 生命周期:卸载时清理
onUnmounted(() => {
clearInterval(timer.value);
clearTimeout(autoScrollTimer.value);
if (wrapperRef.value) {
wrapperRef.value.removeEventListener('scroll', handleScroll);
}
});
</script>
<style scoped>
.notice-scroll-wrapper {
width: 100%;
height: 200px; /* 默认高度,可通过父组件覆盖 */
border: 1px solid #e5e5e5;
border-radius: 4px;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin; /* Firefox 滚动条样式 */
scrollbar-color: #ccc #f5f5f5;
}
/* 自定义滚动条 - Chrome/Edge/Safari */
.notice-scroll-wrapper::-webkit-scrollbar {
width: 6px;
}
.notice-scroll-wrapper::-webkit-scrollbar-track {
background: #f5f5f5;
border-radius: 3px;
}
.notice-scroll-wrapper::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 3px;
}
.notice-scroll-wrapper::-webkit-scrollbar-thumb:hover {
background: #999;
}
.notice-scroll-list {
list-style: none;
padding: 0 20px;
margin: 0;
}
.notice-scroll-item {
line-height: 1.6;
padding: 8px 0;
color: #333;
word-break: break-all;
}
.notice-scroll-item:hover {
color: #1890ff;
transition: color 0.2s;
}
</style>
调用示例:
<template>
<div class="demo-container">
<h3>公告滚动示例</h3>
<!-- 使用公告滚动组件 -->
<NoticeScroll
:list="noticeList"
:autoScrollSpeed="1"
:wheelStep="40"
:pauseOnHover="true"
style="width: 500px; height: 200px;"
/>
</div>
</template>
<script setup>
import NoticeScroll from './NoticeScroll.vue';
// 公告列表数据
const noticeList = [
'【公告1】系统将于2025-12-30 23:00进行维护升级,预计耗时2小时,维护期间将暂停所有线上服务,敬请谅解。',
'【公告2】新用户注册即可领取88元新人礼包,包含5张满减券+1张免运费券,有效期7天,仅限首次注册用户使用。',
'【公告3】企业版新增数据导出功能,支持Excel/PDF格式,可导出近3个月的客户跟进数据、成交数据、报表数据等。',
'【公告4】本周累计成交满10000元,可享9折优惠,优惠可叠加会员权益,活动截止至2025-12-31。',
'【公告5】移动端APP已更新至v2.8.0版本,新增扫码核销、离线缓存功能,建议所有用户及时升级。',
'【公告6】客服工作时间调整为9:00-22:00,节假日正常值班,如有问题可随时联系在线客服。',
'【公告7】会员等级体系升级,新增钻石会员等级,可享专属客服、优先发货等权益。'
];
</script>
<style scoped>
.demo-container {
padding: 50px;
}
</style>