#javascript #reactjs #slider
#javascript #reactjs #слайдер
Вопрос:
У меня есть почти рабочее решение в изолированной среде, связанное с обновлением ниже, вы должны начать с этого.
У меня есть тот, который создан без использования 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. Но каждая ручка может скользить только до середины, где, как и в примере без реакции, меньший ползунок просто не может быть выше верхнего ползунка и наоборот.
Я также хотел бы каким-то образом сделать ползунок, который находится за пределами диапазона кнопок, более темного цвета.
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, приведенное ниже, я смог создать двусторонний слайдер, который вы можете просмотреть в этой изолированной среде, но с ним все еще есть несколько проблем.
- Оранжевый не всегда выглядит так, как будто он перекрывает серый
Иногда, непредсказуемо, эта разница очень заметна
- Когда обе части большого пальца перетаскиваются к краям, одна из них толкает другую вниз
- Кажется, я не могу удалить этот контур, который не всегда появляется
Я протестировал это в firefox
Комментарии:
1. В Google Chrome он имеет другой стиль. Должен ли он быть оранжевого цвета посередине и серого цвета по бокам?
Ответ №1:
Чтобы react работал, вам нужно переместить avg в состояние компонента и во время события input 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>
);
};
ОБНОВЛЕНО
Кажется, я не могу удалить этот контур, который не всегда появляется
Эта строка появляется, потому что браузер обнаруживает, что ввод недопустим. Чтобы отключить этот эффект, используйте:
input[type="range"]:invalid {
box-shadow: none;
}
Оранжевый не всегда выглядит так, как будто он перекрывает серый
И
Когда обе части большого пальца перетаскиваются к краям, одна из них толкает другую вниз
Я не могу воспроизвести этот сценарий, но я рекомендую вам управлять цветом фона, используя минимальное и максимальное положение:
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}%`
}
};
Комментарии:
1. Я поместил ваш слайдер в изолированную среду кода , и он сложный, а также у обеих частей большого пальца все еще есть min и max
2. Да, я вижу, что он ведет себя по-разному в зависимости от браузера, я добавил значение свойства, чтобы избежать этого. смотрите: codesandbox.io/s/rough-silence-dt73v?file=/src/App.js
3. Я заметил еще одну ошибку, извините, когда вы растягиваете элементы большого пальца до их минимального и максимального значений, один из них становится неактивным
4. О, не волнуйтесь, идея в том, что он работает нормально, я обновил код, думаю, теперь он выглядит лучше, codesandbox.io/s/rough-silence-dt73v?file=/src/App.js
5. Идеальный. И код в этой изолированной среде соответствует коду в вашем ответе?
Ответ №2:
Проблема заключалась в том, что min
and max
являются строками, поэтому вычисление avg
было неправильным, но при использовании parseInt
вы получите целые числа, как и ожидалось. здесь я создал изолированную среду, но мне пришлось изменить значение width
from 30
to 300
, чтобы освободить достаточно места для точек.
Комментарии:
1. Спасибо, я посмотрел на это, но была еще одна проблема, что каждый палец мог скользить только до середины. Если вы посмотрите на простой пример javascript, этого не произойдет