vue实现滚动选择组件
Vue 滚动选择组件实现
滚动选择组件常用于日期、时间或自定义选项的选择,以下是一个基于 Vue 3 的实现方案:
核心实现思路
-
数据结构设计 使用数组存储可选数据项,例如年份、月份或自定义选项:
const items = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); -
DOM 结构 创建三层叠加的滚动区域模拟无限滚动效果:
<div class="picker-container"> <div class="picker-mask"></div> <div class="picker-content" ref="content"> <div v-for="(item, index) in items" :key="index">{{ item }}</div> </div> <div class="picker-mask"></div> </div> -
CSS 关键样式 需要固定高度和滚动行为:

.picker-container { height: 200px; overflow: hidden; position: relative; } .picker-content { height: 100%; overflow-y: scroll; scroll-snap-type: y mandatory; } .picker-mask { position: absolute; height: 40%; width: 100%; pointer-events: none; }
完整组件实现
<template>
<div class="scroll-picker">
<div class="picker-container">
<div class="picker-mask top"></div>
<div
class="picker-content"
ref="content"
@scroll="handleScroll"
>
<div
v-for="(item, index) in items"
:key="index"
class="picker-item"
>
{{ item }}
</div>
</div>
<div class="picker-mask bottom"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const props = defineProps({
items: {
type: Array,
required: true
}
});
const content = ref(null);
const selectedIndex = ref(0);
const handleScroll = () => {
const element = content.value;
const itemHeight = element.children[0]?.clientHeight || 40;
const scrollPosition = element.scrollTop;
selectedIndex.value = Math.round(scrollPosition / itemHeight);
};
const scrollToIndex = (index) => {
const element = content.value;
const itemHeight = element.children[0]?.clientHeight || 40;
element.scrollTo({
top: index * itemHeight,
behavior: 'smooth'
});
};
onMounted(() => {
scrollToIndex(selectedIndex.value);
});
defineExpose({
scrollToIndex
});
</script>
<style scoped>
.scroll-picker {
width: 100%;
}
.picker-container {
height: 200px;
overflow: hidden;
position: relative;
}
.picker-content {
height: 100%;
overflow-y: scroll;
scroll-snap-type: y mandatory;
}
.picker-item {
height: 40px;
display: flex;
align-items: center;
justify-content: center;
scroll-snap-align: center;
}
.picker-mask {
position: absolute;
width: 100%;
pointer-events: none;
background: linear-gradient(
to bottom,
rgba(255,255,255,0.9),
rgba(255,255,255,0.4),
transparent
);
}
.picker-mask.top {
top: 0;
height: 40%;
}
.picker-mask.bottom {
bottom: 0;
height: 40%;
background: linear-gradient(
to top,
rgba(255,255,255,0.9),
rgba(255,255,255,0.4),
transparent
);
}
</style>
高级功能扩展
-
惯性滚动优化 添加 momentum 滚动效果:
let startY = 0; let startTime = 0; const handleTouchStart = (e) => { startY = e.touches[0].clientY; startTime = Date.now(); }; const handleTouchEnd = (e) => { const endY = e.changedTouches[0].clientY; const endTime = Date.now(); const distance = endY - startY; const duration = endTime - startTime; const velocity = distance / duration; if (Math.abs(velocity) > 0.1) { const element = content.value; const currentScroll = element.scrollTop; const itemHeight = element.children[0]?.clientHeight || 40; const momentumDistance = velocity * 1000; const targetScroll = currentScroll + momentumDistance; const targetIndex = Math.round(targetScroll / itemHeight); scrollToIndex(Math.max(0, Math.min(targetIndex, props.items.length - 1))); } }; -
边界检测 添加滚动边界限制:
const checkBoundary = () => { const element = content.value; if (element.scrollTop <= 0) { element.scrollTop = 0; } const maxScroll = element.scrollHeight - element.clientHeight; if (element.scrollTop >= maxScroll) { element.scrollTop = maxScroll; } }; -
多列联动 实现多列选择器联动:

const columns = ref([ { items: [2020, 2021, 2022, 2023], selected: 0 }, { items: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], selected: 0 } ]); const updateDependentColumns = (changedIndex) => { if (changedIndex === 0) { // 当年份变化时更新月份列 } };
第三方库推荐
-
BetterScroll 高性能滚动解决方案:
npm install @better-scroll/core -
Vant 的 Picker 组件 已经封装好的移动端选择器:
npm install vant -
SmoothPicker 专门为 Vue 设计的平滑选择器:
npm install vue-smooth-picker
实现滚动选择组件时需要考虑性能优化、触摸交互和视觉反馈,上述方案提供了基础实现和扩展思路,可根据实际需求进行调整。






