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

1.动画文件.vue

javascript 复制代码
<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(内容显示)与动画无关

javascript 复制代码
<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>
相关推荐
Gazer_S22 分钟前
【解析 ECharts 图表样式继承与自定义】
前端·信息可视化·echarts
剪刀石头布啊25 分钟前
视觉格式化模型
前端·css
一 乐28 分钟前
招聘信息|基于SprinBoot+vue的招聘信息管理系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·招聘系统
念九_ysl29 分钟前
Vue3 + ECharts 数据可视化实战指南
前端·信息可视化·echarts
Gazer_S33 分钟前
【Auto-Scroll-List 组件设计与实现分析】
前端·javascript·数据结构·vue.js
前端加油站1 小时前
前端开发人员必备的Mac应用
前端
庸俗今天不摸鱼1 小时前
【万字总结】前端全方位性能优化指南(四)——虚拟DOM批处理、文档碎片池、重排规避
前端·性能优化·dom
harry7591 小时前
React 18+ 安全访问浏览器对象终极指南:从原理到生产级解决方案
前端·javascript
稻城亚丁有沉香1 小时前
Objective-C 方法缓存和清理机制分析
前端
Riesenzahn1 小时前
不用第三方库,说说纯js怎么实现读取和导出excel?
前端·javascript