React中如何将鞋码Range Slider的步长从1改为0.5?
解决React双范围滑块步长设置为0.5的问题
需求:将鞋码范围滑块(数值范围34-50)的步长从1调整为0.5,支持选择34.5、35.5这类半码值,但当前滑动时仅能得到34、35、36这类整数值。尝试过调整百分比计算逻辑无效,提供实现代码如下:
import React, { useCallback, useEffect, useState, useRef } from "react"; import classnames from "classnames"; import PropTypes from "prop-types"; import { FilterIcon } from "@heroicons/react/solid"; const MultiRangeShoeSizeSlider = ({ min, max, onChange }) => { const [minVal, setMinVal] = useState(min); const [maxVal, setMaxVal] = useState(max); const minValRef = useRef(null); const maxValRef = useRef(null); const range = useRef(null); // Convert to percentage const getPercent = useCallback( (value) => Math.round(((value - min) / (max - min)) * 100), [min, max] ); // Set width of the range to decrease from the left side useEffect(() => { if (maxValRef.current) { const minPercent = getPercent(minVal); const maxPercent = getPercent(+maxValRef.current.value); // Preceding with '+' converts the value from type string to type number if (range.current) { range.current.style.left = `${minPercent}%`; range.current.style.width = `${maxPercent - minPercent}%`; } } }, [minVal, getPercent]); // Set width of the range to decrease from the right side useEffect(() => { if (minValRef.current) { const minPercent = getPercent(+minValRef.current.value); const maxPercent = getPercent(maxVal); if (range.current) { range.current.style.width = `${maxPercent - minPercent}%`; } } }, [maxVal, getPercent]); // Get min and max values when their state changes useEffect(() => { onChange({ min: minVal, max: maxVal }); }, [minVal, maxVal, onChange]); return ( <div className="flex flex-col"> <div className="flex flex-row"> {" "} <FilterIcon className="ml-1 mr-2 h-6 w-6 text-gray-400" aria-hidden="true" /> <div className="text-sm text-slate-800 font-semibold mb-3"> Shoe Size (EU) </div> </div> <div className="container"> <input type="range" min={min} max={max} value={minVal} ref={minValRef} onChange={(event) => { const value = Math.min(+event.target.value, maxVal - 0.5); setMinVal(value); event.target.value = value.toString(); }} className={classnames("thumb thumb--zindex-3", { "thumb--zindex-5": minVal > max - 100, })} /> <input type="range" min={min} max={max} value={maxVal} ref={maxValRef} onChange={(event) => { const value = Math.max(+event.target.value, minVal + 0.5); setMaxVal(value); event.target.value = value.toString(); }} className="thumb thumb--zindex-4" /> <div className="slider"> <div className="slider__track" /> <div ref={range} className="slider__range" /> <div className="slider__left-value">{minVal}</div> <div className="slider__right-value">{maxVal}</div> </div> </div> </div> ); }; MultiRangeShoeSizeSlider.propTypes = { min: PropTypes.number.isRequired, max: PropTypes.number.isRequired, onChange: PropTypes.func.isRequired, }; export default MultiRangeShoeSizeSlider;
解决方案
核心问题在于HTML原生<input type="range">控件的默认步长为1,必须显式设置step属性才能支持半数值间隔选择。具体修改如下:
- 为两个range类型的input标签添加
step={0.5}属性 - 当前代码中状态更新的边界限制(如
maxVal - 0.5、minVal + 0.5)已经适配半码需求,无需调整 - 百分比计算逻辑
getPercent基于值与总范围的比例,和步长无关,无需修改
修改后的关键代码片段:
// 最小值滑块 <input type="range" min={min} max={max} step={0.5} // 新增步长设置 value={minVal} ref={minValRef} onChange={(event) => { const value = Math.min(+event.target.value, maxVal - 0.5); setMinVal(value); event.target.value = value.toString(); }} className={classnames("thumb thumb--zindex-3", { "thumb--zindex-5": minVal > max - 100, })} /> // 最大值滑块 <input type="range" min={min} max={max} step={0.5} // 新增步长设置 value={maxVal} ref={maxValRef} onChange={(event) => { const value = Math.max(+event.target.value, minVal + 0.5); setMaxVal(value); event.target.value = value.toString(); }} className="thumb thumb--zindex-4" />
添加step={0.5}后,滑块即可按照0.5的间隔滑动,同时边界限制会确保两个滑块始终保持至少0.5的间距,完全满足鞋码半码的选择需求。
内容的提问来源于stack exchange,提问作者Mesca
相关产品推荐
相关产品推荐

