CSS实现循环无缝滚动效果

308 阅读1分钟

实现原理

将需要展示的数据复制三份.

第一份数据只需要播放一次,播放完成后固定到动画的结束位置(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>