滚动显示效果
1效果
自动滚动消息列表(上下循环);
鼠标移入暂停滚动,移出继续;
每个 item 有 hover 效果(右移 + 背景变浅);
使用了“复制一份 list”实现无缝循环。
.scroll-content
是整个滚动区域。transform: translateY(${scrollY}px)
控制滚动位置。当滚动到第一份数据末尾时,通过复制的第二份 list实现无缝衔接。
当滚动超过第一份高度时重置
scrollY
。2✅ 完整代码示例
<template><div class="list-boxs"><divclass="scroll-container"ref="scrollContainer"@mouseenter="stopScroll"@mouseleave="startScroll"><divclass="scroll-content":style="{ transform: `translateY(${scrollY}px)` }"><!-- 第一份数据 --><div class="item" v-for="item in list" :key="item.id" @click="goMessages(item)"><div class="top"><img src="/src/assets/images/messages.svg" alt="" /><div class="name-box">{{ item.name }}</div></div><div class="time">{{ item.time }}</div></div><!-- 第二份复制数据实现无缝滚动 --><div class="item" v-for="item in list" :key="'copy-' + item.id" @click="goMessages(item)"><div class="top"><img src="/src/assets/images/messages.svg" alt="" /><div class="name-box">{{ item.name }}</div></div><div class="time">{{ item.time }}</div></div></div></div></div> </template><script setup lang="ts"> import { ref, onMounted, onBeforeUnmount } from 'vue';const scrollContainer = ref<HTMLElement | null>(null); const scrollY = ref(0); const speed = 0.4; // 滚动速度(越大越快) let paused = false; let animationFrameId: number | null = null;// 模拟数据 const list = ref([{ id: 1, name: '系统更新公告:版本1.2上线', time: '2025-10-20 09:00' },{ id: 2, name: '任务提醒:项目A进度待确认', time: '2025-10-20 10:30' },{ id: 3, name: '审批结果:系统B已通过审核', time: '2025-10-20 11:15' },{ id: 4, name: '数据同步完成:2025年10月', time: '2025-10-20 12:00' },{ id: 5, name: '消息通知:您有一条新留言', time: '2025-10-20 13:00' }, ]);const step = () => {if (!paused) {scrollY.value -= speed;const contentHeight = scrollContainer.value?.scrollHeight || 0;const containerHeight = scrollContainer.value?.clientHeight || 0;// 到底后重置,实现无缝循环if (Math.abs(scrollY.value) >= contentHeight / 2) {scrollY.value = 0;}}animationFrameId = requestAnimationFrame(step); };onMounted(() => {animationFrameId = requestAnimationFrame(step); });onBeforeUnmount(() => {if (animationFrameId) cancelAnimationFrame(animationFrameId); });function stopScroll() {paused = true; }function startScroll() {paused = false; }function goMessages(item: any) {console.log('点击消息:', item); } </script><style scoped lang="less"> .list-boxs {height: calc(100% - 46px);margin-top: 25px;.scroll-container {position: relative;height: 100%;overflow: hidden;// 渐隐效果:上下边缘淡出mask-image: linear-gradient(to bottom, transparent, black 10%, black 90%, transparent);-webkit-mask-image: linear-gradient(to bottom, transparent, black 10%, black 90%, transparent);.scroll-content {will-change: transform;}}.item {height: 74px;padding: 12px;border-radius: 8px;background: #f3f9fd;cursor: pointer;transition: transform 0.3s ease, background 0.3s ease;&:not(:last-child) {margin-bottom: 12px;}&:hover {transform: translateX(5px);background: #e6f2fa;}.top {display: flex;align-items: center;img {flex-shrink: 0;width: 16px;height: 16px;margin-right: 7px;}.name-box {width: calc(100% - 23px);overflow: hidden;color: #425974;font-size: 14px;font-weight: 400;line-height: 23px;text-align: left;text-overflow: ellipsis;white-space: nowrap;}}.time {margin-top: 4px;margin-left: 23px;color: #8e97a2;font-size: 14px;font-weight: 400;line-height: 23px;text-align: left;}} } </style>