scrollingData.vue 3.21 KB
<template>
  <div class="scrolling-container" @mouseenter="pauseScroll" @mouseleave="resumeScroll">
    <div :style="{ transform: `translateY(${offset}px)` }" class="scrolling-content">
      <div v-for="(item, index) in dataList" :key="index" class="scrolling-item">
        <span/> {{ item }}
      </div>
      <!-- 复制一份数据实现无缝滚动 -->
      <div v-for="(item, index) in dataList" :key="`copy-${index}`" class="scrolling-item">
        <span/> {{ item }}
      </div>
    </div>
  </div>
</template>

<script setup>
import {ref, onMounted, onUnmounted} from 'vue';

const props = defineProps({
  data: {
    type: Array,
    required: true
  },
  speed: {
    type: Number,
    default: 1 // 滚动速度,数值越大越快
  },
  delay: {
    type: Number,
    default: 1000 // 初始延迟时间(毫秒)
  }
});

const dataList = ref([...props.data] || []);
const offset = ref(0);
const scrollInterval = ref(null);
const isPaused = ref(false);
const containerHeight = ref(0);
const contentHeight = ref(0);
const itemHeight = ref(0);

// 初始化滚动
const initScroll = () => {
  // 清除之前的定时器
  if (scrollInterval.value) {
    clearInterval(scrollInterval.value);
  }
  // 重置位置到第一条数据
  offset.value = 0;

  // 设置定时器
  scrollInterval.value = setInterval(() => {
    if (!isPaused.value) {
      offset.value -= props.speed;
      // 当滚动到内容的一半时,重置位置实现无缝滚动
      if (Math.abs(offset.value) >= contentHeight.value / 2) {
        offset.value += contentHeight.value / 2;
      }
    }
  }, 20);
};

// 暂停滚动
const pauseScroll = () => {
  isPaused.value = true;
};

// 恢复滚动
const resumeScroll = () => {
  isPaused.value = false;
};

onMounted(() => {
  // 延迟启动滚动,确保DOM已渲染
  setTimeout(() => {
    const container = document.querySelector('.scrolling-container');
    const content = document.querySelector('.scrolling-content');
    const firstItem = document.querySelector('.scrolling-item');

    if (container && content && firstItem) {
      containerHeight.value = container.clientHeight;
      contentHeight.value = content.clientHeight;
      itemHeight.value = firstItem.clientHeight;

      // 如果内容高度小于容器高度,不需要滚动
      if (contentHeight.value > containerHeight.value) {
        // 初始位置设置为显示第一条数据
        // offset.value = 0;
        initScroll();
      }
    }
  }, props.delay);
});

onUnmounted(() => {
  if (scrollInterval.value) {
    clearInterval(scrollInterval.value);
  }
});
</script>

<style scoped>
.scrolling-container {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
}

.scrolling-content {
  transition: transform 10ms ease;
  will-change: transform;
}

.scrolling-item {
  font-family: PingFang SC, serif;
  font-weight: 400;
  font-size: calc(17 * 100vw / 1920);
  color: #FFFFFF;
  height: calc(40 * 100vh / 1920);
  margin: calc(8 * 100vw / 1920) 0;

  span {
    display: inline-block;
    width: calc(12 * 100vw / 1920);
    height: calc(12 * 100vw / 1920);
    background-color: #01D7F0;
    transform: rotate(45deg);
    margin-left: calc(3 * 100vw / 1920);
    border-radius: calc(3 * 100vw / 1920);
  }
}


</style>