vue3 + css 列表无限循环滚动+鼠标移入停止滚动+移出继续滚动

1.动画文件.vue

<template>
    <div class="dashboard" @click="setFullScreen">
        <div class="warp-box">
            <el-scrollbar ref="scrollRef" height="100%" @scroll="handelScroll">
                <div class="animation-box" @mouseover="stopAnim" @mouseout="runAnim" v-if="arrList.length"
                    :style="{ animationDuration: animationDuration }" :class="{ stopPlay: animationStopPlay }">
                    <div class="line-item" v-for="(item, index) in arrList" :key="index">
                        <img class="item-bg" src="../../assets/images/dashboard/line-bg.png"/>
                        <div class="item-warp">
                            <div class="item-number">
                                <template v-if="index <= 2">
                                    <img :class="`icon-img img-${index}`" :src="imgArr[index]"/>
                                </template>
                                <template v-else><div class="text">{{ index }}</div></template>
                            </div>
                            <div class="item-avatar">
                                <avatarImg :avatar="item.avatar"/>
                            </div>
                            <div class="item-nickname">{{ item.nickname }}</div>
                            <div class="item-point">{{ item.point }}</div>
                        </div>
                    </div>
                    <!-- 重复一次 实现无逢滚动 -->
                    <div class="line-item" v-for="(item, index) in arrList" :key="index">
                        <img class="item-bg" src="../../assets/images/dashboard/line-bg.png"/>
                        <div class="item-warp">
                            <div class="item-number">
                                <template v-if="index <= 2">
                                    <img :class="`icon-img img-${index}`" :src="imgArr[index]"/>
                                </template>
                                <template v-else><div class="text">{{ index }}</div></template>
                            </div>
                            <div class="item-avatar">
                                <avatarImg :avatar="item.avatar"/>
                            </div>
                            <div class="item-nickname">{{ item.nickname }}</div>
                            <div class="item-point">{{ item.point }}</div>
                        </div>
                    </div>
                </div>
            </el-scrollbar>
        </div>
    </div>
</template>

<script setup>
import p0 from '../../assets/images/dashboard/icon_0.png?v=1'
import p1 from '../../assets/images/dashboard/icon_1.png?v=1'
import p2 from '../../assets/images/dashboard/icon_2.png?v=1'
import avatarImg from './avatarImg.vue'
import { nextTick, onMounted, ref, onUnmounted, reactive } from 'vue';
const animationDuration = ref(null);
const animationStopPlay = ref(false);

const imgArr = reactive([p0, p1, p2]);
const scrollRef = ref(null);
const isLoading = ref(false);
const arrList = ref([
    {
        nickname: '昵称昵称昵称昵称',
        avatar: "",
        point: 5000,
    }
]);

const search = reactive({
    page: 1, limit: 20
});
onMounted(() => {
    nextTick(() => {
        setTimeout(() => {
            setFullScreen(); //设置全屏
        }, 3000)
        //加载列表
        loadList();
    })
});

const handelScroll = (event) => {
    const wrapRef = scrollRef.value.wrapRef;
    let poor = wrapRef.scrollHeight - wrapRef.clientHeight;
    // 判断滚动到底部
    if (event.scrollTop + 20 >= poor) {
        loadList();
    }
}

const loadList = () => {
    console.log('加载更多数据...')
    if(isLoading.value) return;
    isLoading.value = true;
    for (let i = 0; i < 30; i++) {
        arrList.value.push({
            nickname: '昵称昵称昵称昵称',
            avatar: "https://pic.qqans.com/up/2024-6/17183287196520597.jpg",
            point: 5000,
        })
    }
    if (arrList.value.length <= 2) {
        animationStopPlay.value = true
        animationDuration.value = 2 + 's'//动画持续时间
    } else {
        animationStopPlay.value = false
        // 跑马灯动画
        animationDuration.value = arrList.value.length * 2 + 's'
    }
    isLoading.value = false;
}
//设置全屏
const setFullScreen = () => {
    const elem = document.getElementById('app');
    if (elem.requestFullscreen) {
        elem.requestFullscreen();
    } else if (elem.mozRequestFullScreen) { /* Firefox */
        elem.mozRequestFullScreen();
    } else if (elem.webkitRequestFullscreen) { /* Chrome, Safari & Opera */
        elem.webkitRequestFullscreen();
    } else if (elem.msRequestFullscreen) { /* IE/Edge */
        elem.msRequestFullscreen();
    }
}

//鼠标移入暂停动画
const stopAnim = () => {
    animationStopPlay.value = true
}
//鼠标移除继续动画
const runAnim = () => {
    if (arrList.value.length > 2) {
        animationStopPlay.value = false
    }
}
</script>

<style lang="scss" scoped>
.dashboard {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    background-image: url("../../assets/images/dashboard/bg-2.png");
    background-size: cover;
}
.warp-box {
    width: 46%; height: 65%;
    margin: 9.5% 0 0 36.5%;
    padding: 10px 0 10px 10px;
}
.line-item{
    width:100%; position: relative; cursor: pointer;
    font-family: XiangCuiDaZiJi35;
    .item-bg{
        width: 100%; height: auto; display: block;
    }
    .item-warp{
        width: 100%; height:100%; box-sizing: border-box; position: absolute; top: 0; left: 0;
        display: flex; justify-content: flex-start; align-items: center;
    }
}
.item-number{
    text-align: center; width: 16%; height:100%;
    color: #96795c;
    .icon-img{
        margin: 16% 0 0 28%;
        width: 60px; height: auto; display: block;
    }
    .text{
        font-size: 36px;margin: 22% 0 0 0;
    }
}
.item-avatar{
    margin-left: 3%; width: 13%; height:80%;
}
.item-nickname{
    width: 44%;
    font-size: 40px; color: #b99871;
}
.item-point{
    width: 22%;
    font-size: 28px; color: #ffffff; text-align: center;
}

.animation-box{
    animation-name: carousel;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    animation-delay: 0s;
    animation-direction: normal;
    &.stopPlay{
        animation-play-state: paused;
    }
}
@keyframes carousel {
    0% {
        transform: translateY(0%)
    }
    100% {
        transform: translateY(-50%)
    }
}
</style>

2.组件avatarImg.vue(内容显示)与动画无关

<template>
    <div class="image-box">
        <img class="avatar-img" :src="!avatar ? avatarBg : avatar"/>
        <img class="avatar-border" src="../../assets/images/dashboard/avatar-border.png"/>
    </div>
</template>

<script setup>
import avatarBg from '../../assets/images/dashboard/avatar-bg.png?v=1'
import { defineProps } from 'vue';
defineProps({
    avatar: {
        typeof: String,
        default: () => {
            return avatarBg;
        }
    }
})
</script>

<style lang="scss" scoped>
.image-box{
    position: relative;
    .avatar-img{
        width: 80px; height: 80px; display: block;
        transform: rotate(-3deg);
        position: absolute; top: 4px; left: 3px;
    }
    .avatar-border{
        width: 90px; height: 90px; display: block;
        position: absolute; top: 0; left: 0;
    }
}
</style>

<think>嗯,用户想用Vue和ElementUI的表格组件做一个滚动公告,每次滚动一条。首先我需要确认ElementUI的表格组件是否支持滚动效果。记得表格组件本身有滚动功能,但通常是数据过多自动滚动,而不是用户想要的逐条滚动公告。 可能需要用定器来控制数据的更新,模拟滚动的效果。比如,每隔一定间更新数据源,让表格显示下一条公告。不过表格通常用来展示多行数据,如果每次只显示一条,可能更类似于轮播效果。或者用户希望表格内容向上滚动,每次滚动一条,类似marquee的效果。 可能需要使用CSS动画或者JS来控制表格内容的滚动。例如,使用transform或margin-top来实现平滑滚动。但ElementUI的表格结构可能比较复杂,直接操作DOM可能会有困难。 另一个思路是使用el-table的data属性,动态改变表格的数据,结合定器,每次显示一条数据,然后切换下一条。不过这样看起来像是数据在切换,而不是滚动效果。用户可能想要的是视觉上的滚动,而不是数据的切换。 或者,可以固定表格的高度,只显示一行,然后利用CSS的过渡效果,当数据更新,让新数据从底部滚动进来。这需要数据数组每次更新,把最前面的数据到后面,然后触发重新渲染,同应用CSS动画。 可能需要一个单行表格,overflow: hidden,然后通过定器改变数据的顺序,比如将数组的第一个元素到末尾,然后更新数据,这样看起来就像是在滚动。同,使用CSS过渡让行在切换有滑入滑出的效果。 不过,这样的实现可能需要用到<transition-group>来处理列表的过渡效果。或者,在el-table中使用自定义的行样式和过渡效果。但el-table的结构可能不太容易直接应用过渡动画。 另外,用户可能希望公告内容连续不断滚动,每次滚动一条,所以可能需要将数据循环显示。例如,复制一次数据,当滚动到最后一条,回到第一条,实现无缝滚动。 或者,使用marquee标签,但这个标签已经过了,不建议使用。所以还是得用CSS或JS实现。 综合来看,可能的解决方案是:设置一个固定高度的表格,显示单行,数据数组循环遍历,每隔一段间将数组的第一个元素到末尾,同使用CSS过渡动画让行向上滚动。这样每次数据更新,新数据会替换旧数据,并带有过渡效果,看起来像在滚动。 然后,具体实现步骤可能是: 1. 使用el-table组件,设置height属性为单行高度,比如60px,这样只显示一行。 2. data中定义一个数组announcements,包含所有公告。 3. 使用定器,每隔一定间(比如3秒)更新数组,将第一个元素到数组末尾。 4. 使用CSS过渡效果,让表格内容在数据变化产生滚动动画。 5. 可能需要包裹表格内容在一个具有overflow: hidden的容器中,并设置过渡属性。 不过,实际测试可能会遇到问题,比如el-table在数据更新会重新渲染整个表格,导致过渡效果无法应用。这候可能需要自定义显示方式,而不是直接使用el-table的数据绑定。 或者,不使用表格,改用其他元素来模拟表格样式,结合过渡效果。但用户明确要求使用ElementUI的表格组件,所以需要尽量利用el-table的特性。 可能的另一个方法是利用el-table的row-key和vue的过渡组件,在数据变化应用动画。例如,使用<transition>包裹el-table的行,设置进入和离开的动画为向上动。但el-table的结构可能让这变得复杂。 或者,考虑使用CSS的@keyframes动画,让表格内容无限滚动。例如,使用transform: translateY来动内容,然后通过动画循环。但需要计算行高和动画间,让每次滚动一条的间间隔合适。 例如,如果行高是36px,动画在3秒内36px,然后重复。但这样动画是连续的,而用户希望每次滚动一条,间隔一定间。可能需要分步动画,每次动一条,停顿一段间,再继续。 这候,可以使用CSS动画的steps()函数,或者使用JS定器逐步改变transform的值。 不过,这些方法可能需要直接操作DOM,而Vue的数据驱动方式可能不太容易结合。 综合考虑,可能较为简单的方法是:使用一个单行表格,定更新数据,每次显示下一条公告,同使用CSS过渡效果让新数据从下方滑入,旧数据向上滑出。虽然el-table可能不支持这种过渡,但可以尝试用<transition>包裹el-table,或者利用列表的过渡。 或者,参考轮播图的做法,使用两个元素交替显示,并应用过渡效果。例如,显示当前公告和下一条公告,通过绝对定位和过渡实现滚动效果。 可能最终实现的方式是:不使用el-table,而是用自定义的div结构模拟表格样式,结合过渡效果。但用户希望使用ElementUI的表格组件,所以可能需要妥协,找到在el-table上实现的方法。 或者,接受视觉上的切换而非真正的滚动效果,使用定器切换数据,并添加淡入淡出过渡。这可能更简单,但不符合用户的“滚动”要求。 可能用户的需求更接近于新闻的垂直滚动轮播,所以可以参考类似解决方案,结合Vue和ElementUI的表格组件。 经过这些思考,可能的解决步骤是: 1. 设置el-table的高度为单行,隐藏表头,只显示一行数据。 2. 使用定器轮换数据,每次将数组第一个元素到末尾。 3. 使用CSS过渡效果,让表格行在切换有向上滑动的动画。 但实际操作,el-table在数据变化可能不会保留原有行的DOM元素,导致过渡无法生效。因此,可能需要使用<transition-group>来包裹数据项,但el-table内部结构可能不支持。 另一种替代方案是,使用v-for渲染自定义的表格结构,而不是el-table,这样可以更好地控制过渡效果。例如: <template> <div class="announcement-container"> <transition-group name="scroll" tag="div" class="table-body"> <div v-for="(item, index) in visibleAnnouncements" :key="item.id" class="table-row"> {{ item.content }} </div> </transition-group> </div> </template> 然后通过CSS设置.scroll-enter-active和.scroll-leave-active的过渡效果,比如向上动。同,定器每次更新visibleAnnouncements数组,保持只显示一条,并触发过渡。 但用户希望使用ElementUI的表格组件,所以可能需要调整思路,或许结合el-table的样式,但实际内容用其他方式实现滚动。 或者,用户可能可以接受使用el-table,但数据每次只显示一条,通过定器切换,同使用CSS动画让表格内容滚动。例如,在表格外部容器设置overflow: hidden,内部内容通过动画改变margin-top或transform。 例如: <template> <div class="scroll-table"> <el-table :data="currentAnnouncements" height="60" :show-header="false"> <!-- 列定义 --> </el-table> </div> </template> 然后,currentAnnouncements是一个包含当前公告的数组,定器每隔一段间替换为下一个公告,同应用CSS动画让新内容从下方进入。 但这样切换可能没有滚动效果,只是突然变化。所以需要动态改变数据,并且利用过渡效果。 或者,让currentAnnouncements始终显示两条数据,然后通过CSS动画将当前移出,下一行移入。例如,使用绝对定位,让两行重叠,然后通过动画动它们的位置。 这可能需要更复杂的结构和CSS控制,可能超出ElementUI表格的能力范围。 综合来看,可能最可行的方案是使用定器切换数据,并结合CSS过渡效果,尽管效果可能不如真正的滚动流畅,但能实现类似功能。或者,如果用户可以接受非表格元素的解决方案,用自定义结构可能更灵活。 但根据用户的需求,必须使用ElementUI的表格组件,所以需要在此限制下找到方法。可能的实现代码如下: 设置el-table的高度为单行,隐藏表头,数据数组每次只显示一条,定切换,同使用CSS过渡。但由于表格的渲染方式,可能无法应用平滑过渡。这候,可能需要使用Vue的强制更新或key属性来触发重新渲染,同应用CSS动画。 例如,每次数据变化,改变表格的key,强制重新渲染,这样CSS动画可以生效。或者,使用<transition>包裹el-table,设置进入和离开的动画。 不过,这样的方法可能有效。例如: <template> <div class="scroll-container"> <transition name="slide" mode="out-in"> <el-table :key="currentIndex" :data="[currentAnnouncement]" height="60" :show-header="false" > <el-table-column prop="content" label="公告"></el-table-column> </el-table> </transition> </div> </template> 然后,在CSS中定义.slide-enter-active和.slide-leave-active的过渡,比如transform: translateY(100%)到0,和离开transform: translateY(-100%)。这样每次切换,新元素从下方滑入,旧元素向上滑出。 这种方法可能实现逐条滚动的效果。需要调整过渡的CSS,确保方向正确,间合适。 总结步骤: 1. 使用el-table显示单条数据。 2. 使用transition包裹表格,设置切换动画。 3. 定器更新currentAnnouncement和currentIndex,触发过渡效果。 4. 调整CSS实现滑动动画。 这样,用户可以看到每次公告向上滚动的效果。虽然并非真正的连续滚动,但能达到逐条切换的视觉效果,接近滚动公告的需求。</think>以下是使用 Vue 和 ElementUI 实现单条滚动公告的示例代码: ```vue <template> <div class="scroll-container"> <transition name="slide" mode="out-in"> <el-table :key="currentIndex" :data="[currentAnnouncement]" height="60" :show-header="false" style="width: 100%" > <el-table-column prop="content" label="公告内容" /> </el-table> </transition> </div> </template> <script> export default { data() { return { announcements: [ { id: 1, content: &#39;这是第一条公告内容&#39; }, { id: 2, content: &#39;这是第二条公告内容&#39; }, { id: 3, content: &#39;这是第三条公告内容&#39; } ], currentIndex: 0, timer: null } }, computed: { currentAnnouncement() { return this.announcements[this.currentIndex] } }, mounted() { this.startScroll() }, beforeDestroy() { this.stopScroll() }, methods: { startScroll() { this.timer = setInterval(() => { this.currentIndex = (this.currentIndex + 1) % this.announcements.length }, 3000) // 3滚动一次 }, stopScroll() { clearInterval(this.timer) } } } </script> <style> .scroll-container { overflow: hidden; } .slide-enter-active { transition: all 0.5s ease; } .slide-leave-active { transition: all 0.5s ease; position: absolute; } .slide-enter { transform: translateY(100%); } .slide-leave-to { transform: translateY(-100%); } .el-table__body { width: 100% !important; } </style> ``` **实现原理:** 1. **数据驱动:** 使用数组存储公告内容,通过索引切换当前显示内容 2. **过渡动画:** 使用 Vue 的 `<transition>` 组件实现上下滑动效果 3. **定轮换:** 使用 setInterval 定切换当前显示的公告索引 4. **表格优化:** 隐藏表头、固定高度、宽度设置为 100% **主要特性:** - 每 3自动滚动切换(间可调) - 平滑的上下滑动过渡效果 - 循环播放公告内容 - 完美集成 ElementUI 表格样式 **使用说明:** 1. 将公告内容填入 `announcements` 数组 2. 修改 `setInterval` 的间间隔(当前3000 毫秒) 3. 可根据需要调整表格高度和动画速度 4. 支持组件销毁自动清除定器 **扩展建议:** - 添加鼠标悬停暂停功能 - 增加自定义滚动方向(左右/上下) - 支持 HTML 格式的公告内容 - 添加手动切换按钮 这个实现方案保持了 ElementUI 表格的样式特征,同通过 Vue 的过渡系统实现了平滑的滚动效果,能够很好地融入现有项目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web项目开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值