当前位置:首页 > 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 关键样式 需要固定高度和滚动行为:

    vue实现滚动选择组件

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

    vue实现滚动选择组件

    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 设计的平滑选择器:

    npm install vue-smooth-picker

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

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

相关文章

简易实现vue

简易实现vue

简易 Vue 实现思路 核心是数据劫持结合发布-订阅模式,通过 Object.defineProperty 或 Proxy 监听数据变化,触发视图更新。 数据劫持实现 class Observer…

vue slot实现

vue slot实现

vue slot 的实现方法 在 Vue 中,slot 是一种内容分发机制,允许父组件向子组件传递模板内容。以下是几种常见的 slot 实现方式: 默认 slot 默认 slot 是最基础的 sl…

vue实现公式

vue实现公式

在Vue中实现公式展示或计算可以通过多种方式实现,以下为具体方法: 使用模板插值与计算属性 若公式较简单,可直接在模板中使用插值表达式或计算属性。例如计算圆的面积: <template&…

vue实现同步

vue实现同步

Vue 实现同步的方法 在 Vue 中实现同步操作通常涉及数据绑定、状态管理和异步操作的处理。以下是几种常见的方法: 使用计算属性(Computed Properties) 计算属性基于响应式依赖进…

vue实现modal

vue实现modal

Vue 实现 Modal 弹窗组件 使用 Vue 实现 Modal 弹窗组件可以通过多种方式,包括原生 Vue 组件、第三方库或自定义指令。以下是几种常见方法: 原生 Vue 组件实现 创建一个基…

分页实现vue

分页实现vue

分页实现(Vue) 在Vue中实现分页功能通常需要结合前端分页逻辑和后端API支持。以下是两种常见的实现方式: 前端分页实现 适用于数据量较小的情况,直接在客户端完成分页逻辑。 <tem…