vue3 无缝滚动

SeamlessScroll 组件介绍

功能说明

SeamlessScroll 是一个实现无缝滚动效果的 Vue 3 组件,主要功能包括:

  1. 无缝垂直滚动:支持向上或向下无缝滚动内容
  2. 可控制滚动速度 :通过 speed 属性调整滚动速度
  3. 可控制滚动方向 :通过 direction 属性设置向上('up')或向下('down')滚动
  4. 鼠标悬停暂停:鼠标悬停时自动暂停滚动,移开后继续
  5. 动态内容同步:自动同步两个滚动容器的内容,确保无缝衔接
  6. 重置功能 :提供 reset 方法可重置滚动位置

组件源码

js 复制代码
<script setup>
  import { onBeforeUnmount, onMounted, ref } from 'vue'
  
  const props = defineProps({
    speed: {
      type   : Number,
      default: 1000
    },
    direction: {
      type   : String,
      default: 'up'
    }
  })
  
  const box = ref(null)
  const refRoll1 = ref(null)
  const refRoll2 = ref(null)
  let top = 0
  let height = 0
  let shadowHeight = 0
  const sp = props.speed / 1000
  let end = false
  let isMouseHover = false
  
  /**
   * 刷新滚动内容的位置和高度
   * 通过比较两个滚动元素的高度来同步内容
   */
  function refresh () {
    height = refRoll1.value.clientHeight
    shadowHeight = refRoll2.value.clientHeight
  
    if (shadowHeight !== height) {
      refRoll2.value.innerHTML = refRoll1.value.innerHTML
    }
    if (top < 0) {
      if (Math.abs(top) >= height) {
        const tmp = refRoll1.value
        refRoll1.value = refRoll2.value
        refRoll2.value = tmp
        top = 0
      }
      refRoll1.value.style.transform = `translate3d(0,${top}px,0)`
      refRoll2.value.style.transform = `translate3d(0,${height + top}px,0)`
    } else {
      if (Math.abs(top) >= height) {
        const tmp = refRoll1.value
        refRoll1.value = refRoll2.value
        refRoll2.value = tmp
        top = -height
      }
      refRoll1.value.style.transform = `translate3d(0,${top}px,0)`
      refRoll2.value.style.transform = `translate3d(0,${top - height}px,0)`
    }
  }
  
  /**
   * 执行动画帧函数,实现无缝滚动效果
   * 使用requestAnimationFrame来优化动画性能
   */
  function doAnimationFrame () {
    requestAnimationFrame(() => {
      if (end) {
        return
      }
  
      // 鼠标悬停时暂停滚动
      if (isMouseHover) {
        refresh()
        doAnimationFrame()
        return
      }
  
      // 根据容器高度和方向控制滚动
      if (height > box.value.clientHeight) {
        if (props.direction === 'down') {
          top += sp
        }
        if (props.direction === 'up') {
          top -= sp
        }
      } else {
        top = 0
      }
      refresh()
      doAnimationFrame()
    })
  }
  
  /**
   * 鼠标进入容器时的处理函数
   * 设置标志位以暂停滚动
   */
  function onMouseEnter () {
    isMouseHover = true
  }
  
  /**
   * 鼠标离开容器时的处理函数
   * 恢复滚动状态
   */
  function onMouseLeaver () {
    isMouseHover = false
  }

  // 组件挂载后开始执行动画
  onMounted(doAnimationFrame)
  
  // 组件销毁前设置结束标志,停止动画
  onBeforeUnmount(() => {
    end = true
  })
  
  /**
   * 重置滚动位置到初始状态
   */
  function reset () {
    top = 0;
    refresh();
  }
  
  // 暴露reset方法供外部调用
  defineExpose({
    reset
  })
  
  </script>
  <template>
    <div
      ref="box"
      class="hb-admin-roll-list-com"
      @mouseenter="onMouseEnter"
      @mouseleave="onMouseLeaver"
    >
      <!-- 滚动内容容器1 -->
      <div
        ref="refRoll1"
        class="roll-item"
      >
        <div class="list-item">
          <slot />
        </div>
      </div>
      <!-- 滚动内容容器2(用于实现无缝滚动效果) -->
      <div
        ref="refRoll2"
        class="roll-item"
      />
    </div>
  </template>
  <style scoped>
  .hb-admin-roll-list-com{
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
  }
  
  .hb-admin-roll-list-com::-webkit-scrollbar{
    display: none;
  }
  
  .roll-item{
    width: 100%;
    position: absolute;
  }
  
  .list-item{
  }
  </style>

使用方法

基本用法

vue 复制代码
<template>
  <SeamlessScroll :speed="800" direction="up">
    <div>滚动内容1</div>
    <div>滚动内容2</div>
    <div>滚动内容3</div>
  </SeamlessScroll>
</template>

<script setup>
import SeamlessScroll from './components/SeamlessScroll.vue'
</script>

属性说明

属性名 类型 默认值 说明
speed Number 1000 滚动速度,数值越大滚动越慢
direction String 'up' 滚动方向,可选 'up' 或 'down'

方法说明

方法名 说明
reset() 重置滚动位置到初始状态

调用方法示例

vue 复制代码
<template>
  <SeamlessScroll ref="scrollRef" :speed="1200" direction="down">
    <div v-for="item in list" :key="item.id">
      {{ item.content }}
    </div>
  </SeamlessScroll>
  <button @click="resetScroll">重置滚动</button>
</template>

<script setup>
import { ref } from 'vue'
import SeamlessScroll from './components/SeamlessScroll.vue'

const scrollRef = ref(null)

const resetScroll = () => {
  scrollRef.value.reset()
}
</script>

工作原理

该组件通过以下方式实现无缝滚动效果:

  1. 创建两个相同的滚动容器(refRoll1refRoll2
  2. 使用 requestAnimationFrame 实现高性能动画
  3. 通过 CSS transform: translate3d() 控制两个容器的位置
  4. 当一个容器完全移出视野时,将其移动到另一个容器的末尾
  5. 通过不断更新 top 值实现滚动效果
  6. 利用 clientHeight 计算容器高度,确保内容无缝衔接

注意事项

  1. 确保父容器有明确的高度设置
  2. 滚动内容需要有明确的高度
  3. 组件会自动隐藏滚动条
  4. 鼠标悬停可临时暂停滚动效果
相关推荐
子兮曰1 小时前
async/await高级模式:async迭代器、错误边界与并发控制
前端·javascript·github
恋猫de小郭2 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
GIS之路4 小时前
ArcGIS Pro 中的 Notebooks 入门
前端
IT_陈寒5 小时前
React状态管理终极对决:Redux vs Context API谁更胜一筹?
前端·人工智能·后端
lemon_yyds6 小时前
《vue 2 升级vue3 父组件 子组件 传值: value 和 v-model
vue.js
Kagol6 小时前
TinyVue 支持 Skills 啦!现在你可以让 AI 使用 TinyVue 组件搭建项目
前端·agent·ai编程
柳杉6 小时前
从零打造 AI 全球趋势监测大屏
前端·javascript·aigc
simple_lau6 小时前
Cursor配置MasterGo MCP:一键读取设计稿生成高还原度前端代码
前端·javascript·vue.js
睡不着先生6 小时前
如何设计一个真正可扩展的表单生成器?
前端·javascript·vue.js
天蓝色的鱼鱼6 小时前
模块化与组件化:90%的前端开发者都没搞懂的本质区别
前端·架构·代码规范