jQuery UI Slider — усложненный вариант выбора диапазона

Автор: Александр Головко Дата публикации: 20.06.2011

В статье jQuery UI Slider — ползунок выбора диапазона мы рассмотрели простой вариант ползунка, когда масштаб в пределах шкалы повсюду одинаков.

Давай теперь немного усложним задачу. Попробуем сделать вот такой выбор цен:

Выбор диапазона цен с помощью ползунков. Вариант с изменяющимся масштабом.

Задача

Требуется реализовать ползунки выбора цен с такими дополнительными условиями:

  • инпуты и ползунки связанны (изменение значения одних тут же отражается на других);
  • масштаб в пределах шкалы не одинаков: левая половина шкалы от 0 до 1000, правая — от 1000 до 40000;
  • шаг изменения цены в пределах шкалы не одинаков: слева пусть он будет равен 10 единиц, справа — 100;

Дополнительные условия взяты не с потолка. Такая задача встречается в реальных проектах. Подразумевается, что если пользователь-покупатель располагает малой суммой то ему важнее более точно выставить цену. В общем такой ползунок в работе значительно удобнее, чем ползунок с одинаковым масштабом.

Итак, задачу поставили, трудностей себе напридумали. Теперь будем их геройски преодолевать.

Решение

Опять будем использовать плагин jQuery UI Slider. Вернее, решение, полученное на его основе в предыдущей статье на эту тему. Конечно, решение придется несколько модифицировать.

В чем, собственно, загвоздка? А в том, что сам плагин работать с меняющимся вдоль шкалы масштабом не умеет. Совсем. Не умеет и точка.

Будем его этому учить? Э, нет. Это без меня! Уж очень сильно придется лопатить чужой код. Неблагодарное это занятие. Мы пойдем другим путем.

Для того, чтобы стало ясно, что мы не заблудимся, а все-таки сделаем все как надо, можешь сразу посмотреть на готовый результат — наш демо-пример.

Проверено в:

  • IE 6-8
  • Firefox 4
  • Opera 11
  • Safari
  • Chrome

Итак, решение существует и сейчас я тебя с ним познакомлю.

Основная идея

Плагин не умеет менять масштаб, ну и шут с ним. Фишка в том, что настоящее значение ползунка никто никогда не увидит. Циферки-то отображаются в инпутах! И программер потом возьмет значения цен, на которые рассчитывает пользователь, именно из инпутов!

Вот и будем пересчитывать то, что показал ползунок в то что нам нужно, непосредственно при выводе результата в инпут. Само собой, при программной установке ползунка, когда пользователь вбивает циферки в инпут руками, нужно не забыть сделать обратное преобразование.

Немного математики

Первым делом, находим все точки изменения масштаба. В нашем случае, такая точка одна, в реальных проектах может быть несколько. Далее рассчитываем масштаб на каждом участке.

Чтобы не усложнять себе жизнь лишними вычислениями, можно инициализировать ползунок таким образом, чтобы его внутреннее значение было точно равно длине шкалы в пикселях по дизайну. Например, у меня шкала 200 пикселей, значит будет так:

#slider {
	width: 200px;
}
<script type="text/javascript">
jQuery("#slider").slider({
	min: 0,
	max: 200,
	values: [0,200],
	range: true
});
</script>

При таком подходе масштабный коэффициент на каждом участке будет равен количеству единиц, приходящихся на участок, поделить на длину участка в пикселях.

Так как я сам себе рисовал ползунок, у меня все получилось совсем красиво. Левая и правая части одинаковы и занимают по 100 пикселей. Соответственно масштабные коэффициенты получились: слева 10, справа 390.

расчет мастабов

Далее все просто. Реализуем логику. Если значение ползунка меньше точки изменения масштаба в пикселях (у меня это 100), то умножаем его на первый масштабный коэффициент (10) иначе, вычитаем длину первого участка (в пикселях), умножаем на второй коэффициент (390) и прибавляем стоимоть первого участка (1000):

value01 = jQuery("#slider").slider("values",0);
value02 = jQuery("#slider").slider("values",1);
		
if(value01 < 100){
	vL1 = value01*10;
	jQuery("input#minCost").val(vL1);
}
else
{
	R1 = value01 - 100;
	vR1 = 390*R1+1000;
	jQuery("input#minCost").val(vR1);
};

Осталось учесть изменение шага. Помнишь, при постановке задачи мы договаривались, что шаг изменения цены слева будет 10, а справа 100. Все для удобства пользователя.

value01 = jQuery("#slider").slider("values",0);
value02 = jQuery("#slider").slider("values",1);
		
if(value01 < 100){
	vL1 = value01*10;
	vL1 = Math.round(vL1/10) * 10; 
	jQuery("input#minCost").val(vL1);
}
else
{
	R1 = value01 - 100;
	vR1 = 390*R1+1000;
	vR1 = Math.round(vR1/100) * 100;
	jQuery("input#minCost").val(vR1);
};

Кто повнимательнее, конечно увидит, что при расчете vL1 я достаточно бестолково умножаю значение на 10 и тут же на 10 и делю. Не стал сокращать запись для большей наглядности. Ведь не всегда же шаг равен масштабному коэффициенту, это просто мне так повезло.

Опять таки, если точек изменения масштаба дизайнер нарисовал больше одной, нужно будет соответственно увеличить количество ветвей в структуре if-else.

Для второго ползунка делаем такие же расчеты (для экономии места этот момент пропускаю — все абсолютно аналогично).

Осталось позаботиться про обратную связь. В смысле правильно выставить ползунки, если пользователь вводит значение в инпут.

Тут все с точностью до наоборот. Если значение в инпуте меньше точки изменения масштаба (теперь уже не в пикселях, а в стоимости, у меня 1000), то делим значение на масштабный коэффициент первого диапазона. Если больше, то вычитаем цену первого диапазона (1000), делим на масштабный коэффициент второго диапазона (390) и прибавляем длину (в пикселях) первого диапазона (100):

var value1=jQuery("input#minCost").val();
if(value1 < 1000){
	vL1 = value1/10;
	jQuery("#slider").slider("values",0,vL1);
}
else
{
	R1 = value1 - 1000;
	vR1 = Math.round(R1/390+100);
	jQuery("#slider").slider("values",0,vR1);
};

Теперь, чтобы окончательно все стало понятно, предлагаю еще раз посмотреть демо-пример, где весь этот код стоит на своих местах.

Вот так несложные математические расчеты помогли научить плагин решать, казалось бы непосильную для него задачу.

Ложка дегтя

Из-за того, что мы задали обработчики событий slide и stop (см. код примера) при отпускании ползунка часто значение в инпуте немного (в пределах шага) меняется.

В то же время, если не задавать обработчик событию stop из-за округления выставить минимальное и максимальное значение на шкале не получится.