当前位置:首页 > VUE

vue实现滚动选择组件

2026-02-21 00:09:16VUE

Vue 滚动选择组件实现

滚动选择组件常用于日期、时间或自定义选项的选择,以下是一个基于 Vue 3 的实现方案:

核心实现思路

  1. 数据结构设计 使用数组存储可选数据项,例如年份、月份或自定义选项:

    const items = ref([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
  2. 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>
  3. 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>

高级功能扩展

  1. 惯性滚动优化 添加 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)));
      }
    };
  2. 边界检测 添加滚动边界限制:

    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;
      }
    };
  3. 多列联动 实现多列选择器联动:

    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) {
        // 当年份变化时更新月份列
      }
    };

第三方库推荐

  1. BetterScroll 高性能滚动解决方案:

    npm install @better-scroll/core
  2. Vant 的 Picker 组件 已经封装好的移动端选择器:

    npm install vant
  3. SmoothPicker 专门为 Vue 设计的平滑选择器:

    vue实现滚动选择组件

    npm install vue-smooth-picker

实现滚动选择组件时需要考虑性能优化、触摸交互和视觉反馈,上述方案提供了基础实现和扩展思路,可根据实际需求进行调整。

标签: 组件vue
分享给朋友:

相关文章

vue实现关键词轮播

vue实现关键词轮播

Vue 实现关键词轮播 基础实现方案 使用 Vue 的 v-for 和 setInterval 实现基础轮播效果: <template> <div class="keyword…

vue实现截图

vue实现截图

Vue 实现截图的方法 使用 html2canvas 库 安装 html2canvas 库: npm install html2canvas 在 Vue 组件中使用: import html2ca…

vue实现模块

vue实现模块

Vue 实现模块化的方法 Vue 支持多种模块化开发方式,可以根据项目需求选择适合的方案。 使用单文件组件(SFC) 单文件组件是 Vue 最常用的模块化方式,将模板、脚本和样式封装在一个 .vue…

vue实现上传

vue实现上传

Vue 文件上传实现方法 使用 Vue 实现文件上传可以通过原生 HTML5 的 <input type="file"> 元素结合 Vue 的事件处理和数据绑定来实现。 基础实现步骤…

vue实现滚屏

vue实现滚屏

实现滚屏的基本方法 在Vue中实现滚屏效果可以通过多种方式完成,常见的有原生JavaScript滚动方法、第三方库或CSS动画。以下是几种常见实现方式: 使用window.scrollTo或Elem…

vue实现watch

vue实现watch

监听数据变化 在Vue中,watch用于监听响应式数据的变化并执行回调函数。基本语法如下: watch: { propertyName(newValue, oldValue) { //…