优雅实现!自定义滚动刻度选择器,精准选择无压力

在移动端开发中,滚动刻度选择器 是一个很常见的需求。他主要体现在体重、血糖、身高等选择。今天,我将分享一个高度自定义的滚动刻度选择器组件,它不仅支持整数和小数两种模式,还提供了流畅的滚动体验和精准的刻度定位。

🌟 组件亮点

  1. 双模式支持:无缝切换整数和小数选择模式
  2. 精准吸附:滚动结束后自动吸附到最近刻度
  3. 视觉层次:主刻度、中等刻度和次刻度清晰区分
  4. 边界处理:滚动到两端时自动锁定边界值
  5. 响应式设计:完美适配不同屏幕尺寸

🚀 实际效果展示

🔧 核心实现思路

1. 刻度系统设计

js 复制代码
// 刻度线类型定义
enum ScaleMarkerType {
  MAJOR = 'major',   // 主刻度(长线)
  MEDIUM = 'medium', // 中等刻度
  MINOR = 'minor'    // 次刻度(短线)
}

// 动态生成刻度系统
const initializeScale = () => {
  scaleMarkers.value = []
  const range = normalizedMaxValue.value - normalizedMinValue.value
  const steps = Math.floor(range / stepSize.value)

  for (let i = 0; i <= steps; i++) {
    const currentValue = normalizedMinValue.value + i * stepSize.value
    
    // 智能识别刻度类型
    let markerType: ScaleMarkerType
    if (props.isIntegerScale) {
      markerType = currentValue % 10 === 0 
        ? ScaleMarkerType.MAJOR 
        : currentValue % 5 === 0 
          ? ScaleMarkerType.MEDIUM 
          : ScaleMarkerType.MINOR
    } else {
      markerType = Number.isInteger(currentValue) 
        ? ScaleMarkerType.MAJOR 
        : currentValue % 1 === 0.5 
          ? ScaleMarkerType.MEDIUM 
          : ScaleMarkerType.MINOR
    }
    
    scaleMarkers.value.push({ type: markerType })
  }
}

2. 滚动位置与数值的精准映射

js 复制代码
// 处理滚动事件
const handleScroll = (event: any) => {
  const scrollOffset = event.detail.scrollLeft
  const validOffset = Math.max(0, Math.min(scrollOffset, maxScrollLeft.value))
  
  // 计算当前值 = 最小值 + (偏移量/刻度宽度)*步长
  const steps = Math.round(validOffset / markerWidth.value)
  const calculatedValue = normalizedMinValue.value + steps * stepSize.value
  
  // 确保值在[min,max]范围内
  const clampedValue = Math.min(
    Math.max(calculatedValue, normalizedMinValue.value),
    normalizedMaxValue.value
  )

  // 根据模式格式化输出
  const finalValue = props.isIntegerScale 
    ? Math.round(clampedValue) 
    : Number(clampedValue.toFixed(1))

  emit('valueChange', finalValue)
}

3. 滚动结束自动吸附

js 复制代码
// 滚动结束处理(自动吸附到最近刻度)
const handleScrollEnd = () => {
  const currentPos = initialScrollPosition.value
  const nearestStep = Math.round(currentPos / markerWidth.value)
  initialScrollPosition.value = nearestStep * markerWidth.value
}

🎨 视觉层次设计

通过CSS实现清晰的视觉层次:

js 复制代码
.scale-marker {
  display: inline-block;
  width: 2px;
  background: #d2d2d2;
  position: relative;

  /* 不同类型刻度高度 */
  &.major { height: 40rpx; } /* 主刻度最高 */
  &.medium { height: 40rpx; }
  &.minor { height: 26rpx; } /* 次刻度最短 */
}

/* 中央指示线样式 */
.indicator-line {
  position: absolute;
  width: 6rpx;
  height: 55rpx;
  top: 0;
  background: var(--wui-color-theme, #007aff);
  left: 50%;
  transform: translateX(-50%);
  border-radius: 6rpx;
  z-index: 10; /* 确保在最上层 */
}

💡 使用示例

js 复制代码
<template>
  <ScrollPicker 
    :initial-value="weight"
    :min-value="0"
    :max-value="200"
    :marker-spacing="12"
    :is-integer-scale="true"
    @value-change="handleWeightChange"
  />
</template>

<script setup>
import { ref } from 'vue'
import ScrollPicker from '@/components/ScrollPicker.vue'

const weight = ref(65)

const handleWeightChange = (newValue) => {
  weight.value = newValue
  console.log(`当前体重: ${newValue}kg`)
}
</script>

🏆 性能优化点

  1. 虚拟滚动:只渲染可视区域内的刻度,大幅提升性能
  2. 防抖处理:滚动事件添加防抖,避免频繁触发重绘
  3. 边界缓存:预计算边界值,避免滚动时重复计算
  4. CSS硬件加速:使用transform提升动画性能

🌈 应用场景

身高、体重、血糖、血压等

相关推荐
鱼樱前端15 分钟前
uni-app快速入门章法(二)
前端·uni-app
silent_missile29 分钟前
vue3父组件和子组件之间传递数据
前端·javascript·vue.js
IT_陈寒2 小时前
Vue 3.4 实战:这7个Composition API技巧让我的开发效率飙升50%
前端·人工智能·后端
少年阿闯~~3 小时前
HTML——1px问题
前端·html
Mike_jia3 小时前
SafeLine:自托管WAF颠覆者!一键部署守护Web安全的雷池防线
前端
brzhang3 小时前
把网页的“好句子”都装进侧边栏:我做了个叫 Markbox 的收藏器,开源!
前端·后端·架构
VincentFHR5 小时前
Canvas 高性能K线图,支持无限左右滑动
前端·数据可视化·canvas
sophie旭5 小时前
一道面试题,开始性能优化之旅(3)-- DNS查询+TCP(二)
前端·面试·性能优化
面向星辰5 小时前
css选择器(继承补充)
前端·css
koooo~5 小时前
Vue3中的依赖注入
前端·javascript·vue.js