Auto-Scroll-List 组件设计与实现分析
https://gitee.com/chennaiyuan/dayup-record/tree/master/一些组件/auto-scroll-list
1. 组件概述
我们封装的 AutoScrollList
是一个自动滚动列表组件,主要用于展示需要自动循环滚动的数据项,如通知、告警、任务等信息。该组件采用了组件与逻辑分离的设计思路,通过自定义 Hook 实现核心滚动逻辑,提高了代码的可复用性和灵活性。除了依赖 Vue3 和 Less 这种常规组件,可以开封即用。
2. 架构设计
组件采用了"关注点分离"的设计理念,将 UI 表现与业务逻辑分开:
AutoScrollList 组件 UI 表现层 useAutoScroll Hook 模板结构 样式定义 滚动状态管理 位置计算 时间控制 使用方
核心架构特点:
- 组件与逻辑分离 :核心滚动逻辑被抽象到
useAutoScroll
Hook 中 - 可组合性:Hook 可独立使用,也可以集成在组件中
- 插槽设计:通过 Vue 的插槽系统实现内容的高度自定义
实现逻辑与数据流
数据流转 AutoScrollList 组件 useAutoScroll Hook 接收参数 定义 定义 定义 生成 计算 设置 修改 清除 调用 调用 传递参数 获取 获取 获取 获取 获取 使用 使用 使用 暴露 暴露 触发 改变 更新 更新 用户数据 定时器 调用Hook Props定义 displayItems isSliding getItemPosition startScroll stopScroll 模板渲染 方法暴露 状态管理 初始化配置 currentIndex isSliding timer displayItems 计算属性 getItemPosition 位置计算 startScroll 控制方法 stopScroll onMounted 生命周期 onBeforeUnmount
核心方法和状态详解
input manages computes AutoScrollOptions +Array items +number itemHeight +number? itemGap +number visibleItems +number? scrollInterval +number? transitionDuration +boolean? autoScroll useAutoScrollReturn +Ref displayItems +Ref isSliding +Ref currentIndex +Function getItemPosition +Function startScroll +Function stopScroll InternalState +Ref<number> currentIndex +Ref<boolean> isSliding +Ref<number|null> timer DisplayItem +original item properties +string key +boolean preload
3. 核心实现逻辑
useAutoScroll Hook
typescript
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
export interface AutoScrollOptions {
items: any[]; // 数据项数组
itemHeight: number; // 单项高度
itemGap?: number; // 项目间距
visibleItems: number; // 可见项目数
scrollInterval?: number; // 滚动间隔(毫秒)
transitionDuration?: number; // 过渡动画时长(毫秒)
autoScroll?: boolean; // 是否自动滚动
}
export function useAutoScroll(options: AutoScrollOptions) {
const {
items,
itemHeight,
itemGap = 0,
visibleItems,
scrollInterval = 3000,
transitionDuration = 500,
autoScroll = true
} = options;
// 状态管理
const currentIndex = ref(0);
const isSliding = ref(false);
const timer = ref<number | null>(null);
// 计算属性
const displayItems = computed(() => {
const result = [];
const totalItems = items.length;
if (totalItems === 0) return [];
// 当前显示的项目
for (let i = 0; i < visibleItems + 1; i++) {
const index = (currentIndex.value + i) % totalItems;
result.push({
...items[index],
key: `${items[index].id}-${index}-${i}`, // 确保key的唯一性
preload: i === visibleItems // 标记预加载项
});
}
return result;
});
// 计算位置
const getItemPosition = (index: number) => {
return index * (itemHeight + itemGap);
};
// 控制方法
const startScroll = () => {
if (timer.value || items.length <= visibleItems) return;
timer.value = window.setInterval(() => {
isSliding.value = true;
setTimeout(() => {
currentIndex.value = (currentIndex.value + 1) % items.length;
isSliding.value = false;
}, transitionDuration);
}, scrollInterval);
};
const stopScroll = () => {
if (timer.value) {
clearInterval(timer.value);
timer.value = null;
}
};
// 生命周期钩子
onMounted(() => {
if (autoScroll && items.length > visibleItems) {
startScroll();
}
});
onBeforeUnmount(() => {
stopScroll();
});
return {
displayItems,
isSliding,
currentIndex,
getItemPosition,
startScroll,
stopScroll
};
}
滚动原理与动画流程
组件挂载 startScroll 定时器 状态变更 视图更新 调用开始滚动 设置定时器 设置isSliding为true 触发动画过渡 预加载项变为可见 延时后更新currentIndex 设置isSliding为false 更新显示项列表 预加载项准备下一轮 循环执行... 组件挂载 startScroll 定时器 状态变更 视图更新
组件实现
vue
<template>
<div
class="auto-scroll-list"
:style="{ height: `${containerHeight}px` }"
>
<template v-if="displayItems.length > 0">
<slot
v-for="(item, index) in displayItems"
:key="item.key"
name="item"
:item="item"
:position="getItemPosition(index)"
:is-sliding="isSliding"
:is-preload="item.preload"
></slot>
</template>
<template v-else>
<slot name="empty"></slot>
</template>
</div>
</template>
<script setup lang="ts">
import { useAutoScroll } from './useAutoScroll';
const props = defineProps({
items: {
type: Array,
required: true
},
itemHeight: {
type: Number,
required: true
},
itemGap: {
type: Number,
default: 0
},
containerHeight: {
type: Number,
required: true
},
visibleItems: {
type: Number,
required: true
},
scrollInterval: {
type: Number,
default: 3000
},
transitionDuration: {
type: Number,
default: 500
},
autoScroll: {
type: Boolean,
default: true
}
});
const {
displayItems,
isSliding,
getItemPosition,
startScroll,
stopScroll
} = useAutoScroll({
items: props.items,
itemHeight: props.itemHeight,
itemGap: props.itemGap,
visibleItems: props.visibleItems,
scrollInterval: props.scrollInterval,
transitionDuration: props.transitionDuration,
autoScroll: props.autoScroll
});
// 暴露方法
defineExpose({
startScroll,
stopScroll
});
</script>
<style lang="less" scoped>
.auto-scroll-list {
position: relative;
overflow: hidden;
}
</style>
4. 使用示例
以下是组件的三种典型使用场景:
基础用法
vue
<auto-scroll-list
:items="notificationItems"
:item-height="80"
:container-height="250"
:visible-items="3"
>
<template #item="{ item, position, isSliding, isPreload }">
<div
class="notification-item"
:style="{ transform: `translateY(${position}px)` }"
:class="{ sliding: isSliding, preload: isPreload }"
>
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.content }}</div>
</div>
</template>
<template #empty>
<div class="empty-message">暂无通知</div>
</template>
</auto-scroll-list>
自定义样式的告警列表
vue
<auto-scroll-list
:items="alertItems"
:item-height="80"
:item-gap="10"
:container-height="250"
:visible-items="3"
:scroll-interval="5000"
>
<template #item="{ item, position, isSliding, isPreload }">
<div
class="alert-item"
:class="{
'high-priority': item.priority === 'high',
'medium-priority': item.priority === 'medium',
'low-priority': item.priority === 'low',
sliding: isSliding,
preload: isPreload
}"
:style="{ transform: `translateY(${position}px)` }"
>
<div class="alert-badge">{{ item.priority === 'high' ? '!' : '⚠' }}</div>
<div class="alert-content">
<div class="alert-title">{{ item.title }}</div>
<div class="alert-message">{{ item.message }}</div>
</div>
</div>
</template>
</auto-scroll-list>
直接使用 Hook 自定义实现
vue
<template>
<div class="custom-list" :style="{ height: `${containerHeight}px` }">
<div
v-for="(item, index) in displayItems"
:key="item.key"
class="task-item"
:style="{ transform: `translateY(${getItemPosition(index)}px)` }"
:class="{ sliding: isSliding, preload: item.preload }"
>
<!-- 自定义内容 -->
</div>
</div>
</template>
<script setup>
import { useAutoScroll } from './useAutoScroll';
// 自定义实现
const containerHeight = 250;
const {
displayItems,
getItemPosition,
isSliding,
startScroll,
stopScroll
} = useAutoScroll({
items: taskItems.value,
itemHeight: 80,
itemGap: 10,
visibleItems: 3,
autoScroll: true
});
</script>
用户交互过程
onMounted自动触发 用户调用stopScroll 用户调用startScroll 组件卸载时 初始状态 自动滚动 scrollInterval时间后 isSliding = true transitionDuration时间后 currentIndex + 1 等待间隔 开始过渡 过渡中 更新索引 暂停滚动
5. 技术优劣分析
优势
- 关注点分离:将滚动逻辑与UI表现分离,提高代码可维护性
- 高度复用性:Hook 可独立使用,适用于不同场景
- 良好的扩展性:通过插槽系统支持高度自定义的内容
- 配置灵活:支持多种滚动参数配置,适应不同业务需求
- 无外部依赖:不依赖第三方库,减少项目体积
劣势
- 性能考虑:对于大量数据,需要考虑虚拟列表优化
- 动画限制:当前仅支持垂直方向滚动,水平滚动需额外开发
- 复杂场景适应性:对于需要拖拽或交互复杂的场景支持有限
- 不支持嵌套列表:当前设计不适合嵌套滚动列表的场景
- 浏览器兼容性:使用了现代CSS特性,可能需要额外的兼容处理
性能分析
性能影响因素 数据量 DOM操作 计算复杂度 动画效果 列表项超过100个时可能出现性能问题 使用absolute定位减少重排 O(n)复杂度,n为可见项+1 使用CSS transform提高动画性能
6. 可改进方向
当前版本 性能优化 滚动方向支持 交互增强 动画扩展 辅助功能 虚拟列表 懒加载 水平滚动 多方向滚动 拖拽支持 排序功能 多种过渡效果 自定义动画 键盘导航 Screen Reader优化
技术路线演进
- 虚拟列表支持:对大数据量进行优化,只渲染可视区域的数据
- 水平滚动支持:扩展当前的垂直滚动逻辑,支持水平方向滚动
- 更多交互方式:添加拖拽、手势支持等交互方式
- 动画多样化:提供更多滚动动画效果选择
- 响应式支持增强:更好地适应不同设备和屏幕尺寸
- 无障碍支持:增加对屏幕阅读器的支持,提高可访问性
7. 总结
AutoScrollList
组件通过组件与逻辑分离的设计,实现了一个灵活、可复用的自动滚动列表解决方案。它的核心价值在于:
- 简化复杂逻辑:封装了滚动、位置计算、过渡动画等复杂逻辑
- 提高开发效率:通过简单配置即可实现自动滚动效果
- 保持灵活性:支持多种自定义方式,适应不同业务场景
以下是组件实现的关键技术点:
虽然存在一些局限性,但对于通知、公告、提醒等信息轮播的场景,该组件提供了一个简洁而有效的解决方案。通过未来的迭代优化,可以进一步提升组件的适用范围和性能表现。