vue实现数据栏无缝滚动实现方式-demo

效果

方式一

通过实现两个item 进行循环

javascript 复制代码
<!--
 * @Author: Jackie
 * @Date: 2023-08-16 21:27:42
 * @LastEditTime: 2023-08-16 21:41:51
 * @LastEditors: Jackie
 * @Description: scroll  水平滚动 - 效果基本满足需求
 * @FilePath: /vue3-swiper-demo/src/components/scroll/Scroll12.vue
 * @version: 
-->
<template>
  <div class="ticker-container">
    <div class="ticker-viewer">
      <div class="ticker-scroll">
        <div
          class="ticker-scroll-item"
          :key="index"
          v-for="(item, index) in items"
        >
          <span v-if="item">{{ item }} ${{ item }}</span>
        </div>
      </div>
      <div class="ticker-scroll">
        <div
          class="ticker-scroll-item"
          :key="index"
          v-for="(item, index) in items"
        >
          <span v-if="item">{{ item }} ${{ item }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const items = ref(Array.from({ length: 26 }, (_, index) => index + 1));
</script>

<style lang="scss" scoped>
.ticker-container {
  align-items: center;
  background: linear-gradient(90deg, #86eef1, #bcff2f 138.82%);
  display: flex;
  height: 50px;
  white-space: nowrap;
  width: 100%;
  .ticker-viewer {
    align-items: center;
    cursor: pointer;
    display: flex;
    height: 100%;
    overflow: hidden;
    position: relative;
    width: 100%;
    .ticker-scroll {
      animation-delay: 8s;
      opacity: 0;

      animation: tickerScroll 240s linear infinite normal;
      transform: translateX(0);

      transition: all linear;
      will-change: transform, opacity;
      .ticker-scroll-item {
        display: inline-block;
        font-size: 18px;
        line-height: 22px;
        color: #fff;
        padding: 0 24px;
      }
    }
  }
}

@keyframes tickerScroll {
  0% {
    opacity: 1;
    transform: translateX(0);
  }

  to {
    opacity: 1;
    transform: translateX(-100%);
  }
}

@keyframes positionScroll {
  0% {
    transform: translateX(3.33333333%);
  }

  to {
    transform: translateX(0);
  }
}
</style>

方式二

通过实现两个item 进行循环

javascript 复制代码
<!--
 * @Author: Jackie
 * @Date: 2023-08-16 21:02:42
 * @LastEditTime: 2023-08-16 21:42:04
 * @LastEditors: Jackie
 * @Description: 过渡 - 通过两次滚动实现 flex  - 效果基本满足需求
 * @FilePath: /vue3-swiper-demo/src/components/scroll/Scroll11.vue
 * @version: 
-->
<template>
  <div class="scroll">
    <div class="price-band-group" ref="group">
      <div
        class="price-band-item DIN-medium"
        :key="index"
        v-for="(item, index) in items"
      >
        <span v-if="item">{{ item }} ${{ item }}</span>
      </div>
    </div>
    <div class="price-band-group" ref="group">
      <div
        class="price-band-item DIN-medium"
        :key="index"
        v-for="(item, index) in items"
      >
        <span v-if="item">{{ item }} ${{ item }}</span>
      </div>
    </div>
  </div>
</template>

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

const scroller = ref(null);
const group = ref(null);
const timerId = ref(null);
const items = ref(Array.from({ length: 26 }, (_, index) => index + 1));
</script>

<style lang="scss" scoped>
.scroll {
  display: flex;
  overflow: hidden;
  width: 100%;
  height: 48px;
  background: linear-gradient(90deg, #31daff 0.47%, #316bff 100%);
  padding: 13px 0;
  box-sizing: border-box;
  overflow: auto;
  .price-band-group {
    display: flex;
    white-space: nowrap;
    /* animation: index_loop 100s linear infinite normal;
    animation-delay: 0.5s; */
    animation: tickerScroll 240s linear infinite normal;
    transform: translateX(0);
    .price-band-item {
      display: inline-block;
      font-size: 18px;
      line-height: 22px;
      color: #fff;
      padding: 0 24px;
    }
  }
  &::-webkit-scrollbar {
    display: none;
  }
}

@keyframes index_loop {
  0% {
    transform: translateZ(0);
  }

  to {
    transform: translate3d(-50%, 0, 0);
  }
}

@keyframes tickerScroll {
  0% {
    opacity: 1;
    transform: translateX(0);
  }

  to {
    opacity: 1;
    transform: translateX(-100%);
  }
}
</style>

方式三

通过尾部填充重复数据实现

javascript 复制代码
<template>
  <div class="scroll" ref="scroller" :style="style">
    <div class="price-band-group" v-if="items.length">
      <div
        class="price-band-item DIN-medium"
        v-for="(item, index) in itemsWithTail"
        :key="index"
      >
        <span v-if="item !== null"> {{ item }} ${{ item }} </span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue';
const scroller = ref(null);
const timerId = ref(null);
const items = ref(Array.from({ length: 12 }, (_, index) => index + 1));
const pauseAnimate = () => {
  timerId.value && clearInterval(timerId.value);
  timerId.value = null;
};
const playAnimate = () => {
  pauseAnimate();
  const maxScrollLeft = scroller.value.scrollWidth - scroller.value.clientWidth;
  console.log(maxScrollLeft);
  timerId.value = setInterval(() => {
    if (scroller.value.scrollLeft >= maxScrollLeft) {
      scroller.value.scrollLeft -= maxScrollLeft;
    }
    scroller.value.scrollLeft += 1;
  }, 33);
};

const itemsWithTail = computed(
  () =>
    items.value.length >= 10 ? items.value.concat(items.value) : items.value
  //   items.value.concat(items.value.slice(0, 4))
);

onMounted(() => {
  setTimeout(() => {
    scroller.value.scrollLeft = 0;
    playAnimate();
  }, 100);
});

onUnmounted(() => {
  pauseAnimate();
});
</script>

<style lang="scss" scoped>
.scroll {
  width: 100%;
  height: 48px;
  background: linear-gradient(90deg, #31daff 0.47%, #316bff 100%);
  padding: 13px 0;
  box-sizing: border-box;
  overflow: hidden;
  transition: scroll-left 1s ease-in-out; /* 添加过渡效果 */
  .price-band-group {
    display: inline-block;
    white-space: nowrap;
    .price-band-item {
      display: inline-block;
      font-size: 18px;
      line-height: 22px;
      color: #fff;
      padding: 0 24px;
    }
  }
  &::-webkit-scrollbar {
    display: none;
  }
}
</style>

方式四

滚动到头后,直接归0

javascript 复制代码
<template>
  <div class="scroll" ref="scroller" :style="style">
    <div class="price-band-group">
      <div class="price-band-item DIN-medium" v-for="item in items" :key="item">
        <span v-if="item"> {{ item }} ${{ item }} </span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const scroller = ref(null);
const timerId = ref(null);
const scrollLeftEnd = ref(false);
const items = ref(Array.from({ length: 22 }, (_, index) => index + 1));
const pauseAnimate = () => {
  timerId.value && clearInterval(timerId.value);
  timerId.value = null;
};
const playAnimate = () => {
  pauseAnimate();
  const maxScrollLeft = scroller.value.scrollWidth - scroller.value.clientWidth;
  console.log(maxScrollLeft);
  timerId.value = setInterval(() => {
    if (scroller.value.scrollLeft >= maxScrollLeft) {
      scroller.value.scrollLeft -= maxScrollLeft;
    }
    scroller.value.scrollLeft += 1;
  }, 33);
};

onMounted(() => {
  console.log(111);
  // 必须在100ms后进行,否则计算不准确
  setTimeout(() => {
    scroller.value.scrollLeft = 0;
    playAnimate();
  }, 100);
});
onUnmounted(() => {
  pauseAnimate();
});
</script>

<style lang="scss" scoped>
.scroll {
  width: 100%;
  height: 48px;
  background: linear-gradient(90deg, #31daff 0.47%, #316bff 100%);
  padding: 13px 0;
  box-sizing: border-box;
  overflow: auto;
  .price-band-group {
    display: inline-block;
    white-space: nowrap;
    .price-band-item {
      display: inline-block;
      font-size: 18px;
      line-height: 22px;
      color: #fff;
      padding: 0 24px;
    }
  }
  &::-webkit-scrollbar {
    display: none;
  }
}
</style>

方式五

来回滚动方式

javascript 复制代码
<template>
  <div class="scroll" ref="scroller" :style="style">
    <div class="price-band-group">
      <div class="price-band-item DIN-medium" v-for="item in 26" :key="item">
        <span v-if="item"> {{ item.item }} ${{ item }} </span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const scroller = ref(null);
const timerId = ref(null);
const scrollLeftEnd = ref(false);
const pauseAnimate = () => {
  timerId.value && clearInterval(timerId.value);
  timerId.value = null;
};
const playAnimate = () => {
  pauseAnimate();
  const maxScrollLeft = scroller.value.scrollWidth - scroller.value.clientWidth;
  console.log(maxScrollLeft);
  timerId.value = setInterval(() => {
    if (scroller.value.scrollLeft >= maxScrollLeft - 1) {
      scrollLeftEnd.value = true;
    }
    if (scroller.value.scrollLeft <= 1) {
      scrollLeftEnd.value = false;
    }
    if (scrollLeftEnd.value) {
      scroller.value.scrollTo({
        top: 0,
        left: (scroller.value.scrollLeft -= 1),
        behavior: 'smooth'
      }); //scrollLeft -= 1;
    } else {
      scroller.value.scrollTo({
        top: 0,
        left: (scroller.value.scrollLeft += 1),
        behavior: 'smooth'
      }); //scrollLeft += 1;
    }
  }, 33);
};

onMounted(() => {
  console.log(111);
  // 必须在100ms后进行,否则计算不准确
  setTimeout(() => {
    scroller.value.scrollLeft = 0;
    playAnimate();
  }, 100);
});
onUnmounted(() => {
  pauseAnimate();
});
</script>

<style lang="scss" scoped>
.scroll {
  width: 100%;
  height: 48px;
  background: linear-gradient(90deg, #31daff 0.47%, #316bff 100%);
  padding: 13px 0;
  box-sizing: border-box;
  overflow: auto;
  .price-band-group {
    display: inline-block;
    white-space: nowrap;
    .price-band-item {
      display: inline-block;
      font-size: 18px;
      line-height: 22px;
      color: #fff;
      padding: 0 24px;
    }
  }
  &::-webkit-scrollbar {
    display: none;
  }
}
</style>

方式六

scroll滚动 - 循环滚动,不超出不滚动

javascript 复制代码
<!--
 * @Author: Jackie
 * @Date: 2023-08-17 10:55:21
 * @LastEditTime: 2023-08-17 11:39:17
 * @LastEditors: Jackie
 * @Description: scroll滚动 - 循环滚动,不超出不滚动
 * @FilePath: /vue3-swiper-demo/src/components/scroll/Scroll13.vue
 * @version: 
-->
<template>
  <div class="ticker-container">
    <div class="ticker-viewer" ref="scrollWiewer">
      <div
        class="ticker-scroll"
        ref="scrollContainer"
        :class="{ 'scroll-animation': !shouldScroll }"
        :style="{ animationDuration: animationDuration }"
      >
        <div
          class="ticker-scroll-item"
          :key="index"
          v-for="(item, index) in items"
        >
          <span v-if="item">{{ item }} ${{ item }}</span>
        </div>
      </div>
      <div
        class="ticker-scroll"
        v-if="shouldScroll"
        :style="{ animationDuration: animationDuration }"
      >
        <div
          class="ticker-scroll-item"
          :key="index"
          v-for="(item, index) in items"
        >
          <span v-if="item">{{ item }} ${{ item }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

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

const items = ref(Array.from({ length: 26 }, (_, index) => index + 1));
const shouldScroll = ref(false);
const scrollContainer = ref(null);
const scrollWiewer = ref(null);

// 计算动画持续时间
const animationDuration = computed(() => {
  const itemCount = items.value.length;
  // 根据元素数量调整倍数
  const durationMultiplier = 2; // 调整这个值以适应您的需求
  return `${itemCount * durationMultiplier}s`;
});

onMounted(() => {
  const wiewerWidth = scrollWiewer.value.offsetWidth;

  const containerWidth = scrollContainer.value.offsetWidth;
  const contentWidth = scrollContainer.value.scrollWidth;

  console.log(containerWidth, wiewerWidth, containerWidth > wiewerWidth);
  shouldScroll.value = containerWidth > wiewerWidth;
});

onUnmounted(() => {
  shouldScroll.value = false;
});
</script>

<style lang="scss" scoped>
.ticker-container {
  align-items: center;
  background: linear-gradient(90deg, #86eef1, #bcff2f 138.82%);
  display: flex;
  height: 50px;
  white-space: nowrap;
  width: 100%;

  .ticker-viewer {
    align-items: center;
    cursor: pointer;
    display: flex;
    height: 100%;
    overflow: hidden;
    position: relative;
    width: 100%;

    .ticker-scroll {
      /* opacity: 0; */

      transition: all linear;
      will-change: transform, opacity;

      /* animation: tickerScroll 20s linear infinite normal; */
      animation-name: tickerScroll;
      animation-timing-function: linear;
      animation-iteration-count: infinite;
      animation-direction: normal;
      transform: translateX(0);
      /* 没有超出不滚动 */
      &.scroll-animation {
        animation: none; // stopScroll 1s linear 1 normal;
        transform: translateX(0);
      }

      .ticker-scroll-item {
        display: inline-block;
        font-size: 18px;
        line-height: 22px;
        color: #fff;
        padding: 0 24px;
      }
    }
  }
}

@keyframes tickerScroll {
  0% {
    opacity: 1;
    transform: translateX(0);
  }

  to {
    opacity: 1;
    transform: translateX(-100%);
  }
}

@keyframes stopScroll {
  0% {
    transform: translateX(0);
  }
}

@keyframes positionScroll {
  0% {
    transform: translateX(3.33333333%);
  }

  to {
    transform: translateX(0);
  }
}
</style>
相关推荐
高山我梦口香糖16 分钟前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_7482352419 分钟前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240251 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar1 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人2 小时前
前端知识补充—CSS
前端·css
GISer_Jing2 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245522 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v2 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing2 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600953 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js