React 中的双面输入滑块
Double sided input slider in React
我在下面的更新中链接的沙箱中有一个几乎可以工作的解决方案,你应该从这个开始。
我有一个没有使用 React 创建的(单击“运行 代码片段”)
var thumbsize = 14;
function draw(slider,splitvalue) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var thumbsize = parseInt(slider.getAttribute('data-thumbsize'));
var rangewidth = parseInt(slider.getAttribute('data-rangewidth'));
var rangemin = parseInt(slider.getAttribute('data-rangemin'));
var rangemax = parseInt(slider.getAttribute('data-rangemax'));
/* set min and max attributes */
min.setAttribute('max',splitvalue);
max.setAttribute('min',splitvalue);
/* set css */
min.style.width = parseInt(thumbsize + ((splitvalue - rangemin)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
max.style.width = parseInt(thumbsize + ((rangemax - splitvalue)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
min.style.left = '0px';
max.style.left = parseInt(min.style.width)+'px';
/* correct for 1 off at the end */
if(max.value>(rangemax - 1)) max.setAttribute('data-value',rangemax);
/* write value and labels */
max.value = max.getAttribute('data-value');
min.value = min.getAttribute('data-value');
}
function init(slider) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var rangemin = parseInt(min.getAttribute('min'));
var rangemax = parseInt(max.getAttribute('max'));
var avgvalue = (rangemin + rangemax)/2;
/* set data-values */
min.setAttribute('data-value',rangemin);
max.setAttribute('data-value',rangemax);
/* set data vars */
slider.setAttribute('data-rangemin',rangemin);
slider.setAttribute('data-rangemax',rangemax);
slider.setAttribute('data-thumbsize',thumbsize);
slider.setAttribute('data-rangewidth',slider.offsetWidth);
/* draw */
draw(slider,avgvalue);
/* events */
min.addEventListener("input", function() {update(min);});
max.addEventListener("input", function() {update(max);});
}
function update(el){
/* set function vars */
var slider = el.parentElement;
var min = slider.querySelector('#min');
var max = slider.querySelector('#max');
var minvalue = Math.floor(min.value);
var maxvalue = Math.floor(max.value);
/* set inactive values before draw */
min.setAttribute('data-value',minvalue);
max.setAttribute('data-value',maxvalue);
var avgvalue = (minvalue + maxvalue)/2;
/* draw */
draw(slider,avgvalue);
}
var sliders = document.querySelectorAll('.min-max-slider');
sliders.forEach( function(slider) {
init(slider);
});
.min-max-slider {
position: relative;
width: 200px;
text-align: center;
margin-bottom: 50px;
}
.min-max-slider > label {
display: none;
}
.min-max-slider > input {
cursor: pointer;
position: absolute;
}
/* webkit specific styling */
.min-max-slider > input {
-webkit-appearance: none;
outline: none!important;
background: transparent;
background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}
.min-max-slider > input::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 14px; /* Set a specific slider handle width */
height: 14px; /* Slider handle height */
background: #eee; /* Green background */
cursor: pointer; /* Cursor on hover */
border: 1px solid gray;
border-radius: 100%;
}
.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<div class="min-max-slider" data-legendnum="2">
<label for="min">Minimum price</label>
<input id="min" class="min" name="min" type="range" step="1" min="0" max="3000" />
<label for="max">Maximum price</label>
<input id="max" class="max" name="max" type="range" step="1" min="0" max="3000" />
</div>
我尝试使用 React 创建相同类型的组件。但是每个 thumbgrip 只能滑动到中点,与非反应示例一样,较小的滑块不能高于较高的滑块,反之亦然。
我还想要一些方法,使超出拇指手柄范围的滑块颜色更深。
var thumbsize = 14;
const Slider = ({min, max}) => {
const avg = (min + max)/2;
const width = 300
const minWidth = thumbsize + ((avg - min)/(max - min))*(width - (2*thumbsize))
const styles={
min:{
width: minWidth,
left: 0
},
max:{
width: thumbsize + ((max - avg)/(max - min))*(width - (2*thumbsize)),
left: minWidth
}
}
return (
<div
class="min-max-slider"
data-legendnum="2"
data-rangemin={min}
data-rangemax={max}
data-thumbsize={thumbsize}
data-rangewidth={width}
>
<label htmlFor="min">Minimum price</label>
<input
id="min"
className="min"
style={styles.min}
name="min"
type="range"
step="1"
min={min}
max={avg}
data-value={min}
/>
<label htmlFor="max">Maximum price</label>
<input
id="max"
className="max"
style={styles.max}
name="max"
type="range"
step="1"
min={avg}
max={max}
data-value={max}
/>
</div>
)
}
ReactDOM.render(<Slider min={300} max={3000} />, document.querySelector("#root"))
.min-max-slider {
position: relative;
width: 200px;
text-align: center;
margin-bottom: 50px;
}
.min-max-slider > label {
display: none;
}
.min-max-slider > input {
cursor: pointer;
position: absolute;
}
/* webkit specific styling */
.min-max-slider > input {
-webkit-appearance: none;
outline: none!important;
background: transparent;
background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}
.min-max-slider > input::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 14px; /* Set a specific slider handle width */
height: 14px; /* Slider handle height */
background: #eee; /* Green background */
cursor: pointer; /* Cursor on hover */
border: 1px solid gray;
border-radius: 100%;
}
.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
更新
使用下面的 lissetdm 解决方案,我已经能够创建一个双面滑块,您可以在 this sandbox 上查看它,但它仍然存在一些问题。
- 橙色看起来并不总是与灰色重叠
有时,出乎意料,这种差异非常明显
- 当两个拇指块都被拖到边缘时,一个将另一个向下推
- 我似乎无法删除这个轮廓,它并不总是出现
我已经在 firefox
中测试过了
问题是 min
和 max
是字符串,所以计算 avg
是不正确的,但是通过使用 parseInt
你会得到预期的整数。 here 我创建了一个沙箱,但我不得不将 width
的值从 30
更改为 300
以便为点制作足够的 space。
随着 React 的工作,您需要将 avg 移动到组件的状态,并在输入 onChange 事件期间更新它的值:
const Slider = ({ min, max }) => {
const [avg, setAvg] = useState((min + max) / 2);
const [minVal, setMinVal] = useState(avg);
const [maxVal, setMaxVal] = useState(avg);
const width = 300;
const minWidth =
thumbsize + ((avg - min) / (max - min)) * (width - 2 * thumbsize);
const styles = {
min: {
width: minWidth,
left: 0
},
max: {
width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
left: minWidth
}
};
useEffect(() => {
setAvg((maxVal + minVal) / 2);
}, [minVal, maxVal]);
return (
<div
className="min-max-slider"
data-legendnum="2"
data-rangemin={min}
data-rangemax={max}
data-thumbsize={thumbsize}
data-rangewidth={width}
>
<label htmlFor="min">Minimum price</label>
<input
id="min"
className="min"
style={styles.min}
name="min"
type="range"
step="1"
min={min}
max={avg}
value={minVal}
onChange={({ target }) => setMinVal(Number(target.value))}
/>
<label htmlFor="max">Maximum price</label>
<input
id="max"
className="max"
style={styles.max}
name="max"
type="range"
step="1"
min={avg}
max={max}
value={maxVal}
onChange={({ target }) => setMaxVal(Number(target.value))}
/>
</div>
);
};
已更新
I can't seem to remove this outline, which doesn't always appear
出现此行是因为浏览器检测到输入无效。要禁用此效果,请使用:
input[type="range"]:invalid {
box-shadow: none;
}
The orange doesn't always look like it overlaps the gray
和
When both thumb pieces are dragged to the edges, the one pushes the
other down
我无法重现这种情况,但我建议您使用最小和最大位置来控制背景颜色:
CSS:
input[type="range"] {
--minRangePercent: 0%;
--maxRangePercent: 0%;
height: .4rem;
}
.min-max-slider > input.min {
background-image: linear-gradient(
to right,
silver 0%,
silver var(--minRangePercent),
orange var(--minRangePercent),
orange 100%
);
}
.min-max-slider > input.max {
background-image: linear-gradient(
to right,
orange 0%,
orange var(--maxRangePercent),
silver var(--maxRangePercent),
silver 100%
);
}
JS:
const minPercent = ((minVal - min) / (avg - min)) * 100;
const maxPercent = ((maxVal - avg) / (max - avg)) * 100;
const styles = {
min: {
width: minWidth,
left: 0,
"--minRangePercent": `${minPercent}%`
},
max: {
width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
left: minWidth,
"--maxRangePercent": `${maxPercent}%`
}
};
我在下面的更新中链接的沙箱中有一个几乎可以工作的解决方案,你应该从这个开始。
我有一个没有使用 React 创建的(单击“运行 代码片段”)
var thumbsize = 14;
function draw(slider,splitvalue) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var thumbsize = parseInt(slider.getAttribute('data-thumbsize'));
var rangewidth = parseInt(slider.getAttribute('data-rangewidth'));
var rangemin = parseInt(slider.getAttribute('data-rangemin'));
var rangemax = parseInt(slider.getAttribute('data-rangemax'));
/* set min and max attributes */
min.setAttribute('max',splitvalue);
max.setAttribute('min',splitvalue);
/* set css */
min.style.width = parseInt(thumbsize + ((splitvalue - rangemin)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
max.style.width = parseInt(thumbsize + ((rangemax - splitvalue)/(rangemax - rangemin))*(rangewidth - (2*thumbsize)))+'px';
min.style.left = '0px';
max.style.left = parseInt(min.style.width)+'px';
/* correct for 1 off at the end */
if(max.value>(rangemax - 1)) max.setAttribute('data-value',rangemax);
/* write value and labels */
max.value = max.getAttribute('data-value');
min.value = min.getAttribute('data-value');
}
function init(slider) {
/* set function vars */
var min = slider.querySelector('.min');
var max = slider.querySelector('.max');
var rangemin = parseInt(min.getAttribute('min'));
var rangemax = parseInt(max.getAttribute('max'));
var avgvalue = (rangemin + rangemax)/2;
/* set data-values */
min.setAttribute('data-value',rangemin);
max.setAttribute('data-value',rangemax);
/* set data vars */
slider.setAttribute('data-rangemin',rangemin);
slider.setAttribute('data-rangemax',rangemax);
slider.setAttribute('data-thumbsize',thumbsize);
slider.setAttribute('data-rangewidth',slider.offsetWidth);
/* draw */
draw(slider,avgvalue);
/* events */
min.addEventListener("input", function() {update(min);});
max.addEventListener("input", function() {update(max);});
}
function update(el){
/* set function vars */
var slider = el.parentElement;
var min = slider.querySelector('#min');
var max = slider.querySelector('#max');
var minvalue = Math.floor(min.value);
var maxvalue = Math.floor(max.value);
/* set inactive values before draw */
min.setAttribute('data-value',minvalue);
max.setAttribute('data-value',maxvalue);
var avgvalue = (minvalue + maxvalue)/2;
/* draw */
draw(slider,avgvalue);
}
var sliders = document.querySelectorAll('.min-max-slider');
sliders.forEach( function(slider) {
init(slider);
});
.min-max-slider {
position: relative;
width: 200px;
text-align: center;
margin-bottom: 50px;
}
.min-max-slider > label {
display: none;
}
.min-max-slider > input {
cursor: pointer;
position: absolute;
}
/* webkit specific styling */
.min-max-slider > input {
-webkit-appearance: none;
outline: none!important;
background: transparent;
background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}
.min-max-slider > input::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 14px; /* Set a specific slider handle width */
height: 14px; /* Slider handle height */
background: #eee; /* Green background */
cursor: pointer; /* Cursor on hover */
border: 1px solid gray;
border-radius: 100%;
}
.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<div class="min-max-slider" data-legendnum="2">
<label for="min">Minimum price</label>
<input id="min" class="min" name="min" type="range" step="1" min="0" max="3000" />
<label for="max">Maximum price</label>
<input id="max" class="max" name="max" type="range" step="1" min="0" max="3000" />
</div>
我尝试使用 React 创建相同类型的组件。但是每个 thumbgrip 只能滑动到中点,与非反应示例一样,较小的滑块不能高于较高的滑块,反之亦然。
我还想要一些方法,使超出拇指手柄范围的滑块颜色更深。
var thumbsize = 14;
const Slider = ({min, max}) => {
const avg = (min + max)/2;
const width = 300
const minWidth = thumbsize + ((avg - min)/(max - min))*(width - (2*thumbsize))
const styles={
min:{
width: minWidth,
left: 0
},
max:{
width: thumbsize + ((max - avg)/(max - min))*(width - (2*thumbsize)),
left: minWidth
}
}
return (
<div
class="min-max-slider"
data-legendnum="2"
data-rangemin={min}
data-rangemax={max}
data-thumbsize={thumbsize}
data-rangewidth={width}
>
<label htmlFor="min">Minimum price</label>
<input
id="min"
className="min"
style={styles.min}
name="min"
type="range"
step="1"
min={min}
max={avg}
data-value={min}
/>
<label htmlFor="max">Maximum price</label>
<input
id="max"
className="max"
style={styles.max}
name="max"
type="range"
step="1"
min={avg}
max={max}
data-value={max}
/>
</div>
)
}
ReactDOM.render(<Slider min={300} max={3000} />, document.querySelector("#root"))
.min-max-slider {
position: relative;
width: 200px;
text-align: center;
margin-bottom: 50px;
}
.min-max-slider > label {
display: none;
}
.min-max-slider > input {
cursor: pointer;
position: absolute;
}
/* webkit specific styling */
.min-max-slider > input {
-webkit-appearance: none;
outline: none!important;
background: transparent;
background-image: linear-gradient(to bottom, transparent 0%, transparent 30%, silver 30%, silver 60%, transparent 60%, transparent 100%);
}
.min-max-slider > input::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 14px; /* Set a specific slider handle width */
height: 14px; /* Slider handle height */
background: #eee; /* Green background */
cursor: pointer; /* Cursor on hover */
border: 1px solid gray;
border-radius: 100%;
}
.min-max-slider > input::-webkit-slider-runnable-track {cursor: pointer;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
更新
使用下面的 lissetdm 解决方案,我已经能够创建一个双面滑块,您可以在 this sandbox 上查看它,但它仍然存在一些问题。
- 橙色看起来并不总是与灰色重叠
有时,出乎意料,这种差异非常明显
- 当两个拇指块都被拖到边缘时,一个将另一个向下推
- 我似乎无法删除这个轮廓,它并不总是出现
我已经在 firefox
中测试过了问题是 min
和 max
是字符串,所以计算 avg
是不正确的,但是通过使用 parseInt
你会得到预期的整数。 here 我创建了一个沙箱,但我不得不将 width
的值从 30
更改为 300
以便为点制作足够的 space。
随着 React 的工作,您需要将 avg 移动到组件的状态,并在输入 onChange 事件期间更新它的值:
const Slider = ({ min, max }) => {
const [avg, setAvg] = useState((min + max) / 2);
const [minVal, setMinVal] = useState(avg);
const [maxVal, setMaxVal] = useState(avg);
const width = 300;
const minWidth =
thumbsize + ((avg - min) / (max - min)) * (width - 2 * thumbsize);
const styles = {
min: {
width: minWidth,
left: 0
},
max: {
width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
left: minWidth
}
};
useEffect(() => {
setAvg((maxVal + minVal) / 2);
}, [minVal, maxVal]);
return (
<div
className="min-max-slider"
data-legendnum="2"
data-rangemin={min}
data-rangemax={max}
data-thumbsize={thumbsize}
data-rangewidth={width}
>
<label htmlFor="min">Minimum price</label>
<input
id="min"
className="min"
style={styles.min}
name="min"
type="range"
step="1"
min={min}
max={avg}
value={minVal}
onChange={({ target }) => setMinVal(Number(target.value))}
/>
<label htmlFor="max">Maximum price</label>
<input
id="max"
className="max"
style={styles.max}
name="max"
type="range"
step="1"
min={avg}
max={max}
value={maxVal}
onChange={({ target }) => setMaxVal(Number(target.value))}
/>
</div>
);
};
已更新
I can't seem to remove this outline, which doesn't always appear
出现此行是因为浏览器检测到输入无效。要禁用此效果,请使用:
input[type="range"]:invalid {
box-shadow: none;
}
The orange doesn't always look like it overlaps the gray
和
When both thumb pieces are dragged to the edges, the one pushes the other down
我无法重现这种情况,但我建议您使用最小和最大位置来控制背景颜色:
CSS:
input[type="range"] {
--minRangePercent: 0%;
--maxRangePercent: 0%;
height: .4rem;
}
.min-max-slider > input.min {
background-image: linear-gradient(
to right,
silver 0%,
silver var(--minRangePercent),
orange var(--minRangePercent),
orange 100%
);
}
.min-max-slider > input.max {
background-image: linear-gradient(
to right,
orange 0%,
orange var(--maxRangePercent),
silver var(--maxRangePercent),
silver 100%
);
}
JS:
const minPercent = ((minVal - min) / (avg - min)) * 100;
const maxPercent = ((maxVal - avg) / (max - avg)) * 100;
const styles = {
min: {
width: minWidth,
left: 0,
"--minRangePercent": `${minPercent}%`
},
max: {
width: thumbsize + ((max - avg) / (max - min)) * (width - 2 * thumbsize),
left: minWidth,
"--maxRangePercent": `${maxPercent}%`
}
};