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. 鼠标悬停可临时暂停滚动效果
相关推荐
布列瑟农的星空7 分钟前
大话设计模式——关注点分离原则下的事件处理
前端·后端·架构
山有木兮木有枝_10 分钟前
node文章生成器
javascript·node.js
yvvvy26 分钟前
前端必懂的 Cache 缓存机制详解
前端
北海几经夏41 分钟前
React自定义Hook
前端·react.js
龙在天1 小时前
从代码到屏幕,浏览器渲染网页做了什么❓
前端
TimelessHaze1 小时前
【performance面试考点】让面试官眼前一亮的performance性能优化
前端·性能优化·trae
yes or ok1 小时前
前端工程师面试题-vue
前端·javascript·vue.js
我要成为前端高手1 小时前
给不支持摇树的三方库(phaser) tree-shake?
前端·javascript
Noxi_lumors1 小时前
VITE BALABALA require balabla not supported
前端·vite
周胜22 小时前
node-sass
前端