无限滚动:使用Web Animations API

96 阅读1分钟

需要哪些功能

  1. 可以无限滚动
  2. 可以自定义显示高度
  3. 可以自定义滚动速度
  4. 可以自定义滚动内容

遇到了哪些问题

  1. 滚动到最后一列后,过渡到头部时,会闪烁

实现

seamless-scroll.vue 组件

<script setup>
import { onMounted } from 'vue';
const props = defineProps({
  list: {
    type: Array,
    required: true
  },
  // 每列移动的间隔
  duration: {
    type: Number,
    default: 500
  },
  // 每列高度
  itemHeight: {
    type: Number,
    required: true
  },
  // 可视高度
  boxHeight: {
    type: Number,
    required: true
  }
});
// 1.将数据复制一份放列表末尾
const lookSize = Math.round(props.boxHeight / props.itemHeight); // 可视个数
const listProp = [...props.list, ...props.list.slice(0, lookSize)];

// 设置容器宽高
const setHeight = () => {
  const scroll = document.querySelector('.scroll-container').style;
  scroll.setProperty('--boxHeight', `${props.boxHeight}px`);
  scroll.setProperty('--itemHeight', `${props.itemHeight}px`);
};

const spriteFrames = [];

for (let index = 0; index <= props.list.length; index++) {
  const obj = { transform: `translateY(-${index * props.itemHeight}px)` };
  // 每次滚动一行
  if (index === props.list.length) {
    spriteFrames.push(obj);
  } else {
    // 添加相同帧,创造暂停效果
    spriteFrames.push(obj, obj);
  }
  // 平滑滚动
  // spriteFrames.push(obj);
}
console.log(spriteFrames);
// 添加移动事件
let boxAnimate; // 动画对象
const move = () => {
  const box = document.querySelector('.scroll-box');
  boxAnimate = box.animate(spriteFrames, {
    duration: props.duration * listProp.length,
    iterations: Infinity // 动画次数无限
  });
};
// 暂停和启动
const mouseMove = () => {
  const scroll = document.querySelector('.scroll-container');
  scroll.onmouseover = () => {
    boxAnimate.pause();
  };
  scroll.onmouseout = () => {
    boxAnimate.play();
  };
};

onMounted(() => {
  setHeight();
  move();
  mouseMove();
});
</script>
<template>
  <div class="scroll-container">
    <div class="scroll-box">
      <slot :listProp="listProp"></slot>
    </div>
  </div>
</template>
<style lang="scss" scoped>
.scroll-container {
  --boxHeight: 0px;
  --itemHeight: 0px;
  height: var(--boxHeight);
  line-height: var(--itemHeight);
  overflow: hidden;
}
</style>

组件应用

<script setup>
import SeamlessScroll from '@/components/seamless-scroll/index.vue';
let list = [];
for (let index = 0; index < 10; index++) {
  list.push({ title: `list${index}` });
}
</script>
<template>
  <div class="list-container">
    <SeamlessScroll
      :list="list"
      :duration="1000"
      :item-height="30"
      :box-height="30"
      v-slot="{ listProp }"
    >
      <div v-for="(item, index) in listProp" :key="index">
        {{ item.title }}
      </div>
    </SeamlessScroll>
  </div>
</template>