在移动端开发中,滚动刻度选择器 是一个很常见的需求。他主要体现在体重、血糖、身高等选择。今天,我将分享一个高度自定义的滚动刻度选择器组件,它不仅支持整数和小数两种模式,还提供了流畅的滚动体验和精准的刻度定位。
🌟 组件亮点
- 双模式支持:无缝切换整数和小数选择模式
- 精准吸附:滚动结束后自动吸附到最近刻度
- 视觉层次:主刻度、中等刻度和次刻度清晰区分
- 边界处理:滚动到两端时自动锁定边界值
- 响应式设计:完美适配不同屏幕尺寸
🚀 实际效果展示

🔧 核心实现思路
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>
🏆 性能优化点
- 虚拟滚动:只渲染可视区域内的刻度,大幅提升性能
- 防抖处理:滚动事件添加防抖,避免频繁触发重绘
- 边界缓存:预计算边界值,避免滚动时重复计算
- CSS硬件加速:使用transform提升动画性能
🌈 应用场景
身高、体重、血糖、血压等