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

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

🌟 组件亮点

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

🌈 应用场景

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

相关推荐
一只小灿灿8 分钟前
前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理
前端·opencv·计算机视觉
前端小趴菜0520 分钟前
react状态管理库 - zustand
前端·react.js·前端框架
Jerry Lau1 小时前
go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
前端·golang·gin
我命由我123451 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js
0wioiw01 小时前
Flutter基础(前端教程③-跳转)
前端·flutter
落笔画忧愁e1 小时前
扣子Coze纯前端部署多Agents
前端
海天胜景1 小时前
vue3 当前页面方法暴露
前端·javascript·vue.js
GISer_Jing2 小时前
前端面试常考题目详解
前端·javascript
Boilermaker19922 小时前
【Java EE】SpringIoC
前端·数据库·spring
中微子3 小时前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript