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

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

🌟 组件亮点

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

🌈 应用场景

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

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万4 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋4 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁4 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95274 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大4 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师4 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学5 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端