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

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

🌟 组件亮点

  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提升动画性能

🌈 应用场景

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

相关推荐
长存祈月心10 分钟前
Rust 迭代器适配器
java·服务器·前端
先树立一个小目标15 分钟前
puppeteer生成PDF实践
前端·javascript·pdf
冲刺逆向18 分钟前
【js逆向案例二】瑞数6 深圳大学某医院
前端·javascript·vue.js
啃火龙果的兔子19 分钟前
Promise.all和Promise.race的区别
前端
马达加斯加D22 分钟前
Web身份认证 --- OAuth授权机制
前端
2401_8370885022 分钟前
Error:Failed to load resource: the server responded with a status of 401 ()
开发语言·前端·javascript
全栈师23 分钟前
LigerUI下frm与grid的交互
java·前端·数据库
叫我詹躲躲23 分钟前
被前端存储坑到崩溃?IndexedDB 高效用法帮你少走 90% 弯路
前端·indexeddb
无尽夏_23 分钟前
CSS3(前端基础)
前端·css·1024程序员节
温宇飞24 分钟前
Next.js 简述 - React 全栈框架
前端