实现原理
将需要展示的数据复制三份.
第一份数据只需要播放一次,播放完成后固定到动画的结束位置(transform(translateX(-100%)))
第二份和第三份数据的父元素进行相对定位,数据本身分别绝对定位,定位到元素左上角和父元素的左上角重合。
第二份数据添加动画效果,从本身位置移动到第一份元素位置的两倍,速度曲线为线性,播放次数为无限次。
第三份数据与第二份数据类似,但是需要多添加一个延迟效果,为速度的1/2。
需要注意点
如果只复制两份元素,在重复播放的时候会有闪烁的现象发生。
三份元素也会出现闪烁的现象,不过闪烁现象发生时会被隐藏到展示区域以外。不会被用户看到。
例子代码
<template>
<div class="mt-8 overflow-hidden">
<div class="flex items-center">
<div class="flex w-fit" :class="{ 'move-1': shouldAnimate }">
<div v-for="item in items" :key="item" class="px-4">
<img src="/background.webp" alt="" class="w-44 h-44 rounded-50" />
</div>
</div>
<div class="move flex w-fit h-44" v-if="shouldAnimate">
<div class="move-2 flex w-fit">
<div v-for="item in items" :key="item" class="px-4">
<img src="/background.webp" alt="" class="w-44 h-44 rounded-50" />
</div>
</div>
<div class="move-3 flex w-fit">
<div v-for="item in items" :key="item" class="px-4">
<img src="/background.webp" alt="" class="w-44 h-44 rounded-50" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { $t } from '@/lang/index';
import { onMounted, onUnmounted, ref, computed } from 'vue';
const list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const items = list;
const itemWidth = 44;
const windowWidth = ref(window.innerWidth);
const shouldAnimate = computed(() => {
return items.length * itemWidth >= windowWidth.value - 44 * 2;
});
const handleResize = () => {
windowWidth.value = window.innerWidth;
};
onMounted(() => {
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped lang="scss">
// 无限滚动效果
.move-1 {
animation: move1 10s linear 1 forwards;
}
@keyframes move1 {
from {
transform: translateX(0);
}
to {
transform: translateX(-100%);
}
}
.move {
position: relative;
}
.move-2 {
position: absolute;
left: 0;
top: 0;
animation: move 20s linear infinite;
}
.move-3 {
position: absolute;
left: 0;
top: 0;
animation: move 20s linear infinite;
animation-delay: 10s;
}
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(-200%);
}
}
</style>