vue3组件--无限滚动效果

引言

最近在写项目时简单封装了一个文字向上无限滚动的Vue3组件,现在通过分享的方式让自己再加深一次记忆,本人水平有限,如果有错误的地方或者可以优化的地方还请指出来,感谢!

一、代码展示

<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'

const props = defineProps({
  width: {
    type: [String, Number],
    default: '600px'
  },
  height: {
    type: [String, Number],
    default: '150px'
  },
  customStyle: {
    type: Object,
    default: () => ({})
  },
  items: {
    type: Array,
    required: true
  },
  speed: {
    type: Number,
    default: 600
  },
  scrollSpeed: {
    type: Number,
    default: 1000
  },
  scrollDelay: {
    type: Number,
    default: 100
  }
})

// 响应式数据
const currentItems = ref([...props.items])
const currentOffset = ref(0)
const itemHeight = ref(0)
const transitionEnabled = ref(true)
const wrapper = ref(null)
let timeoutId = null

// 样式计算
const contentStyle = computed(() => ({
  transform: `translateY(${currentOffset.value}px)`,
  transition: transitionEnabled.value ? 
    `transform ${props.scrollSpeed}ms linear` : 'none'
}))

const mergedStyle = computed(() => ({
  width: addUnit(props.width),
  height: addUnit(props.height),
  ...props.customStyle
}))

// 工具方法
function addUnit(val) {
  return typeof val === 'number' ? `${val}px` : val
}

function rotateItems() {
  // 将第一个元素移动到数组末尾
  currentItems.value = [...currentItems.value.slice(1), currentItems.value[0]]
}

// 滚动逻辑
function startScroll() {
  if (!props.items.length) return
  
  // 启用过渡
  transitionEnabled.value = true
  // 滚动一个项目的高度
  currentOffset.value = -itemHeight.value
}

function onTransitionEnd() {
  if (!props.items.length) return

  // 禁用过渡准备重置
  transitionEnabled.value = false
  // 立即重置位置
  currentOffset.value = 0
  // 轮换数组元素
  rotateItems()
  
  nextTick(() => {
    // 稍后重新开始滚动
    timeoutId = setTimeout(startScroll, props.scrollDelay)
  })
}

// 生命周期
onMounted(() => {
  if (props.items.length) {
    nextTick(() => {
      const firstItem = wrapper.value.querySelector('.ticker-item')
      itemHeight.value = firstItem?.offsetHeight || 0
      timeoutId = setTimeout(startScroll, props.scrollDelay)
    })
  }
})

onBeforeUnmount(() => {
  clearTimeout(timeoutId)
})

// 监听数据变化
watch(() => props.items, (newVal) => {
  currentItems.value = [...newVal]
  currentOffset.value = 0
  nextTick(() => {
    const firstItem = wrapper.value?.querySelector('.ticker-item')
    itemHeight.value = firstItem?.offsetHeight || 0
  })
}, { deep: true })
</script>

<template>
  <div 
    :style="mergedStyle" 
    class="ticker"
  >
    <div class="ticker-wrapper" ref="wrapper">
      <div 
        class="ticker-content" 
        :style="contentStyle"
        @transitionend="onTransitionEnd"
      >
        <div
          v-for="(item, index) in currentItems"
          :key="index"
          class="ticker-item"
        >
          <slot :item="item">
            {{ item }}
          </slot>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.ticker {
  border: 1px solid #E7DFDF;
  border-radius: 15px;
  position: relative;
  overflow: hidden;
  background: white;

  &-wrapper {
    height: 100%;
    overflow: hidden;
    position: relative;
  }

  &-content {
    position: relative;
    will-change: transform;
  }

  &-item {
    box-sizing: border-box;
    padding: 8px 15px;
    font-size: 14px;
    color: #333;
    line-height: 1.5;
    min-height: 100%;
    border-bottom: 1px solid #eee;
    background: white;

    &:last-child {
      border-bottom: none;
    }
  }
}
</style>

二、实现逻辑

1. 初始化阶段

props配置处理

defineProps({
  width: { default: '600px' }, // 容器宽度智能处理
  height: { default: '150px' }, // 容器高度类型转换
  items: { required: true },    // 核心数据源
  scrollSpeed: { default: 1000 } // 控制动画流畅度
})
  • 支持数字/字符串类型传值,内部统一处理单位
  • 数据项强制要求保证业务数据完整性

2. 核心响应式数据

滚动位置控制

const currentOffset = ref(0) // 当前滚动偏移量
const itemHeight = ref(0)    // 动态计算的项目高度
  • currentOffset 驱动 CSS transform 变化
  • itemHeight 通过 DOM 查询动态获取

数据队列管理

const currentItems = ref([...props.items]) // 数据副本
function rotateItems() {
  currentItems.value = [...currentItems.value.slice(1), currentItems.value[0]]
}
  • 深拷贝原始数据避免污染源
  • 数组轮换实现循环展示

3. 滚动动画引擎

动画启动机制

function startScroll() {
  transitionEnabled.value = true
  currentOffset.value = -itemHeight.value // 触发CSS过渡
}
  • 激活过渡效果
  • 偏移量设置为单个项目高度

动画周期管理

function onTransitionEnd() {
  transitionEnabled.value = false // 冻结过渡
  currentOffset.value = 0        // 瞬时复位
  rotateItems()                  // 数据轮换
  nextTick(() => {
    timeoutId = setTimeout(startScroll, props.scrollDelay)
  })
}

4、动态适配处理

高度自适应逻辑

onMounted(() => {
  nextTick(() => {
    const firstItem = wrapper.value.querySelector('.ticker-item')
    itemHeight.value = firstItem?.offsetHeight || 0
  })
})
  • 等待DOM渲染完成后获取实际高度
  • 支持动态内容变化后的高度重计算

响应式数据监听

watch(() => props.items, (newVal) => {
  currentItems.value = [...newVal] // 数据同步更新
  currentOffset.value = 0         // 重置滚动位置
})
  • 深度监听保证数据变化及时响应
  • 状态充值确保滚动连贯性

5、扩展接口设计

插槽支持

<slot :item="item">
  {{ item }} <!-- 默认展示方案 -->
</slot>
  • 支持自定义内容模板
  • 作用域插槽暴露数据项

6、逻辑示意图

在这里插入图片描述

三、组件使用

1. 基本使用

使用代码

const newsList = [
  '最新公告:系统将于今晚23:00进行维护升级',
  '促销活动:全场商品5折优惠,限时24小时',
  '安全提示:请定期修改密码以确保账户安全',
  '欢迎新用户:用户ID-2387 刚刚完成注册',
  '天气预报:明日多云转晴,气温22-28℃'
]

<infoTicker :items="newsList" :scrollDelay="0" :width="600"></infoTicker>

效果展示

在这里插入图片描述

2. 插槽使用

使用代码


const newsList2 = ref([
  {
    time: '09:00',
    type: 'success',
    content: '支付成功:用户ID-9823 购买高级会员'
  },
  {
    time: '09:05',
    type: 'warning',
    content: '库存预警:商品SKU-2387 剩余库存不足10件'
  },
  {
    time: '09:10',
    type: 'error',
    content: '服务器响应超时,正在自动重试...'
  },
  {
    time: '09:15',
    type: 'info',
    content: '新版本预告:V2.3.0 即将上线'
  }
])

<infoTicker
  :items="newsList2"
  speed="3000"
  scrollSpeed="800"
  width="600"
  height="60"
>
  <template #default="{ item }">
    <div class="ticker-item" :class="item.type">
      <span class="time">{{ item.time }}</span>
      <span class="dot"></span>
      <span class="content">{{ item.content }}</span>
    </div>
  </template>
</infoTicker>

<style lang="scss" scoped>
.ticker-item {
  display: flex;
  align-items: center;
  padding: 12px;
  font-size: 14px;
}

.time {
  color: #666;
  min-width: 50px;
}

.dot {
  margin: 0 10px;
  color: #999;
}

.success { color: #67c23a; }
.warning { color: #e6a23c; }
.error { color: #f56c6c; }
.info { color: #909399; }
</style>

效果展示

在这里插入图片描述

要实现vue-seamless-scroll的横向无限滚动,可以按照以下步骤进行操作: 1. 首先,需要在Vue项目中引入vue-seamless-scroll组件。可以使用全局组件引入的方式,在main.js或者入口文件中添加以下代码: ``` import scroll from &#39;vue-seamless-scroll&#39; Vue.use(scroll) ``` 2. 其次,需要安装vue-seamless-scroll插件。在项目的根目录下运行以下命令安装vue-seamless-scroll插件: ``` npm install vue-seamless-scroll --save ``` 3. 接着,在需要使用横向无限滚动组件中,可以设置滚动条的属性。可以在computed中设置属性,例如: ```javascript computed: { scrollOptions() { return { step: 1.0, // 数值越大速度滚动越快 hoverStop: true, // 是否开启鼠标悬停停止滚动 direction: 2, // 设置为2表示横向滚动 waitTime: 1000, // 单步运动停止的时间,默认值为1000ms openWatch: true, // 数据实时监控刷新DOM } }, }, ``` 4. 最后,在模板中使用vue-seamless-scroll组件,并将属性绑定到组件上,例如: ```html <scroll :options="scrollOptions"> <!-- 添加需要无限滚动的内容 --> <div>...</div> <div>...</div> <div>...</div> </scroll> ``` 通过以上步骤,你就可以实现vue-seamless-scroll的横向无限滚动效果了。记得根据你的实际需求调整属性的值以及添加需要滚动的内容。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [使用vue-seamless-scroll 来实现无限滚动效果](https://blog.csdn.net/chenw9527/article/details/125947712)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值