jQuery

индекс
283,94

Ползунки или новый взгляд на скрол из песочницы

Как я до этого дошел

Совсем недавно мне достался веб проект дизайн которого рисовал Гуров Сергей вот ссылка на его работу. Один из интересных элементов был скрол на странице каталога (он присутствует на многих страницах). Выглядел он так:

скриншот элементов на странице каталога

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

Принцип работы

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

На чем писать?

За основу я взял свою любимую библиотеку jQuery и её разрешение jQuery UI (Draggable и необходимые для его работы Mouse, Widget, Core). Она мне наиболее симпатизировала, да и старый дизайн сайта был на ней.

Скелет

Создав простую разметку:

 <div id="block-outer">
  <div id="block-for-scroll">
   <div id="block-for-scroll-inter">
    <ul class="style-ul">
     <li class="li li-n-1">li#1 inter text</li>
     <li class="li li-n-2">li#2 inter text</li>
     <li class="li li-n-3">li#3 inter text</li>
     <li class="li li-n-4">li#4 inter text</li>
     <li class="li li-n-5">li#5 inter text</li>
     <li class="li li-n-6">li#6 inter text</li>
     <li class="li li-n-7">li#7 inter text</li>
     <li class="li li-n-8">li#8 inter text</li>
     <li class="li li-n-9">li#9 inter text</li>
     <li class="li li-n-10">li#10 inter text</li>
     <li class="li li-n-11">li#11 inter text</li>
     <li class="li li-n-12">li#12 inter text</li>
     <li class="li li-n-13">li#13 inter text</li>
     <li class="li li-n-14">li#14 inter text</li>
     <li class="li li-n-15">li#15 inter text</li>
     <li class="li li-n-1+">li#16 inter text</li>
     <li class="li li-n-17">li#17 inter text</li>
     <li class="li li-n-18">li#18 inter text</li>
     <li class="li li-n-19">li#19 inter text</li>
     <li class="li li-n-20">li#20 inter text</li>
     <li class="li li-n-21">li#21 inter text</li>
     <li class="li li-n-22">li#22 inter text</li>
     <li class="li li-n-23">li#23 inter text</li>
     <li class="li li-n-24">li#24 inter text</li>
    </ul>
   </div>
  </div>
 </div>
 <div id="scroll-div">
  <div id="scroll-outer">
   <div id="scroll-toddler"></div>
  </div>
 </div>


И придав ей стилей:

*{
margin:0px;
padding:0px;
}
 
#block-outer{
position:relative;
height:200px;
margin:50px;
border:2px dashed blue;
overflow:hidden;
}
 
#block-outer #block-for-scroll{
position:absolute;
left:0px;
top:0px;
height:100%;
width:30000px;
}
 
#block-outer #block-for-scroll #block-for-scroll-inter{
position:absolute;
}
 
#block-for-scroll .style-ul{
list-style:none;
height:100%;
}
 
#block-for-scroll .style-ul .li{
position:relative;
float:left;
margin:5px;
height:186px;
border:2px dotted red;
width:180px;
}
 
#scroll-div{
position:relative;
margin:20px;
}
 
#scroll-div #scroll-outer{
position:relative;
width:300px;
height:20px;
margin:0 auto;
background:gray;
}
 
#scroll-div #scroll-outer #scroll-toddler{
position:absolute;
height:20px;
width:20px;
left:140px;
top:0px;
background:green;
cursor:pointer;
}


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

скелет

Скрипт

Вот эта часть то и самая интересная. Подключив jQuery и jQuery UI начал объявлять переменные и привязывать из к скелету:

$(document).ready(function(){
   //индификатор интервала анимации
   var scrollIntervalVar;
   //отклонение ползунка (интервал от -1 до 1)
   var scrollPercentSpeed = 0;
   //скорость перемещения
   var scrollSpeed = 10;
   //инверсия скролла
   var scrollRevert = false;
   //id блока за который будем перемещать (ползунок)
   var idScrollToddler = "scroll-toddler";
   //id контейнера для этого же блока (внутри него будет перемещатся ползунок)
   var idScrollConteiner = "scroll-outer";
   //этот блок имитирует переремещения скрола
   var idSlideDiv = "block-for-scroll";
   //это наша рамка внутри которой всё будет происходить
   var idSlideOuterDiv = "block-outer";
   //ну и конешно же сам блок внутри которого само содержимое
   var idSlideWidthDiv = "block-for-scroll-inter";
   //ось ползунка (вещь пока не нужная она нам понадобится в следующих постах)
   var scrollToddlerAxis = "x"; 
});


Определив нужные переменные принялся к вычислению и заданию стартового положения ползунка:

$(document).ready(function(){
   //индификатор интервала анимации
   var scrollIntervalVar;
   //отклонение ползунка (интервал от -1 до 1)
   var scrollPercentSpeed = 0;
   //скорость перемещения
   var scrollSpeed = 10;
   //инверсия скролла
   var scrollRevert = false;
   //id блока за который будем перемещать (ползунок)
   var idScrollToddler = "scroll-toddler";
   //id контейнера бля этого же блока (внутри него будет перемещатся ползунок)
   var idScrollConteiner = "scroll-outer";
   //этот блок имитирует переремещения скрола
   var idSlideDiv = "block-for-scroll";
   //это наша рамка внутри которой всё будет происходить
   var idSlideOuterDiv = "block-outer";
   //ну и конешно же сам блок внутри которого само содержимое
   var idSlideWidthDiv = "block-for-scroll-inter";
   //ось ползунка (вещь пока не нужная она нам понадобится в следующих постах)
   var scrollToddlerAxis = "x";
 
   //Вычеслим центр самого ползунка
   //он нам пригодится для центрирования положения относительно миши
   var centerToddler = {'x':$('#'+idScrollToddler).outerWidth(true)/2,'y':$('#'+idScrollToddler).outerHeight(true)/2};
   //Ну и конешно зададим его для него
   document.getElementById(idScrollToddler).style.left = centerToddlerPosition+'px';
   //Возьмем ширину конетейнера для ползунка
   var widthConteiner = $('#'+idScrollConteiner).width();
   //Возьмем ширину ползунка
   var widthToddler = $('#'+idScrollToddler).width();
   //Вычеслим css left для центрального положения ползунка
   var centerToddlerPosition = (widthConteiner-widthToddler)/2;
   //Возьмем ширину внутреностей нашего скрола
   var widthSlideInterDiv = $('#'+idSlideWidthDiv).outerWidth(true);
   //Возьмем ширину нашего скрола
   var widthSlideOuterDiv = $('#'+idSlideOuterDiv).width();
 
   //Вычеслим минимальное значение left для скрола
   var minLeftSlideDiv = widthSlideOuterDiv - widthSlideInterDiv;
   //И зададим максимальное
   var maxLeftSlideDiv = 0;
});


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

//Делаем наш ползунок перетаскиваемым
$('#'+idScrollToddler).draggable({
 //Парамер отчечающий за положение курсора относительно блока
 //Зададим центральное положение для ползунка
 //Эти данные будут равны центру самого ползунка который мы уже определили рание
 cursorAt: {
  top: centerToddler.y,
  left: centerToddler.x
 },
 //Параметр который позволит нам возвращасть ползунок
 //в центральное положение после перемещений
 revert: true,
 //Ось по которой будет перемещатся ползунок
 axis: scrollToddlerAxis,
 //Блок - контейнер, внутри которого это всё будет происходить
 containment: '#'+idScrollConteiner,
 //Функция для события начала перетаскивания
 start: function() {
  //Сразу же создадим интервал для анимирования перемещений
  scrollIntervalVar = setInterval(function(){
   //несложными математическими функциями определим отклонение ползунка
   scrollPercentSpeed = ( $('#'+idScrollToddler).position().left - centerToddlerPosition ) / centerToddlerPosition;
   //получим данные о текощем положении скрола
   var thisLeft = $('#'+idSlideDiv).position().left;
   //Вычеслим новое положение left
   //Положение бует изментся относительно положения ползунка умноженого на скорость
   //Скорость была указана нами в начале скрипта
   //Также применяется scrollRevert (для инверсии)
   if(scrollRevert){
    var newLeft = thisLeft + scrollSpeed * scrollPercentSpeed;
   }else{
    var newLeft = thisLeft - scrollSpeed * scrollPercentSpeed;
   }
   //Чтобы наш скрол имел начало и конец
   //Сравним новое положение с граничными положениями (min&max)
   //И в случае выхода за них задам новое положение
   //Которое будет равно min или max соответственно
   if( newLeft < minLeftSlideDiv ) newLeft = minLeftSlideDiv;
   if( newLeft > maxLeftSlideDiv ) newLeft = maxLeftSlideDiv;
   //Ну и конешно же зададим самому обекту новое положение
   document.getElementById(idSlideDiv).style.left = newLeft+'px';
  },13);
 },
 //Функция для события возврата ползунка после отжимания мишки, в нашем случае.
 stop: function() {
  //Ползунок будет иметь центральное положение то и отклонение тоже должно быть равно 0
  //Зададим ему 0 навсякий случай
  scrollPercentSpeed = 0;
  //Ну и удалим наш интервал, зачем мучать браузер лишними процесами
  clearInterval(scrollIntervalVar);
 }
});


Пока не забыл, время для интервала выбрал именно 13 ( его кстати использует и jQuery для .animate() ).

Итоги

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

P.S. Вскоре опубликую плагин для jQuery с полным функционалом.
P.S. 2 Очень хочу услышать критику об моем хабрапосте и самом скрипте

UPD: jQuery Plugin — Scroll Sliders или Ползунки beta
+51
10 января 2012, 15:18
307

комментарии (76)

+8
datacompboy #
Комментарий на каждую строку?!
+16
ruslansavenok #
Думаю автор это сделал для статьи.

Но по моему скромному мнению людям которые разбираются в этом, проще читать код без комментов.
+6
buna1991 #
Ты абсолютно прав, я пока юный хабраписатель, пока не уверен в нравах других хабраюзеров
+1
MrSLonoed #
Если переменные названы хорошо, а у вас они названы хорошо, то в комментариях необходимости нет
0
buna1991 #
Спасибо за хорошую оценку, я уже взял урок для себя из этой статьи. Впереть буду делать меньше комментариев в коде.
+1
mgrach #
Да уж читать код жуть как неудобно (:
Но видно, что сделано из лучших побуждений
0
AstralMan #
В IE 9 ползунок не двигается
0
buna1991 #
Возможно из-за кодировок заметил что вылил на хост не в той кодировке (щас исправленно)
+7
Saldacenkaw #
мм, не очень логично получается, я хочу на ползунке видеть в какой части страницы я в данный момент, а у Вас он всегда в середине остаётся.
0
buna1991 #
думаю будет логично добавить блок с положением.
PS. есть такое на другом проекте реализовано. Позже будет опубликован плагин.
+1
datacompboy #
может, зациклить их бесконечно? тогда будет удобно так прокручивать.
+1
mrmager #
Тогда будет неудобно их смотреть. Я думаю, пользователю будет удобнее знать, что он долистал до конца, иначе долго придется смотреть на мелькание, пока не станет ясно, что оно зациклено.
0
buna1991 #
Да, мой арт директор и я в проекте тоже пришли к этом логичному заключению.
+1
ksupipr #
соглашусь, что циклить не надо, но всеж при большом количестве товара не помешало б сделать что-то вроде стрелочек «вернутся к началу» или наоборот в конец.
0
tFirma #
Можно сделать на скроллбаре отметку (как на пэйджбаре dirty.ru).
0
buna1991 #
Возможно добавим.
0
tFirma #
Хорошо бы останавливать скролинг не где попало, а с выравниванием по границе товара.
0
buna1991 #
на проекте реализовано. viventa.ru (ещё допиливается)
+1
tFirma #
Почти =) Более приятным поведением было бы «доскроливание» сразу после отпускания кнопки мыши. А у вас сначала «по инерции» скролится, а потом «доскроливается» до границы товара (с другой скоростью и иногда в другую сторону).
+2
buna1991 #
Попробую реализовать, огромное спасибо за комментарий
0
AxelTv #
Да, хотелось бы, чтобы всегда вперед проскролливал. Когда откатывается назад, создается ощущение, что тормозит
0
buna1991 #
интересное мнение
–7
Moxa #
горизонтальный скроллинг — зло!
0
afsherman #
Кто вам такую чушь сказал? Горизонтальный + вертикальный ещё согласен, но горизонтальный вполне уместен в случае, если необходимо сделать а-ля пролистывание страниц.
–1
Sibarit #
А вот опера уже что-то там патентует и видит будущее исключительно в нем. Как быть? =)
0
buna1991 #
Хм, очень интересно. Может кто-то подскажет?
0
AFoST #
Просто скажу. Не знаю, возможно, уже реализовано.
Витает мысль скроллинга аля айфон. Мышь зажимаешь, ведёшь, оно скроллится.
0
buna1991 #
Как уже писал искал несколько дней решение — не найдено.
0
manny #
code.google.com/p/jquery-scrollview/
первая строчка в гугле:)
0
buna1991 #
немного не то
0
manny #
а как должно быть?
0
buna1991 #
Его надо перепиливать
0
manny #
мы ведь оба понимаем, что это ответ на другой вопрос?)
–1
afsherman #
В любом браузере нажимаешь на колёсико и будет именно тот эффект, о котором вы пишете.
0
homm #
Не именно тот.
+1
ilyaerin #
Почти на всех страницах сайта apple.com есть удобная и безглючная горизонтальная листала изображений в шапке.
+1
gigigi #
Немного не то, но есть такая штука для Хрома, называется chromeTouch chrome.google.com/webstore/detail/ncegfehgjifmmpnjaihnjpbpddjjebme можно включать и мышкой скроллить как на тачскрине.
+1
buna1991 #
Очень интересно, наверное она мне сможет помочь в доработке моих скриптов для iPad и других touch устройств.
–2
judicator #
Извините за занудство, но у вас ошибка в слове «дошол». :)
0
buna1991 #
Исправлено до Вас.
0
Seldon #
Мм… не учитывается что окно браузрера могу ресайзить, ну и код не читаем )) много коментов и много переменных )
0
buna1991 #
комментарии были написаны мною как я уже понял по ошибке… надо было их не писать :(
0
lukianol #
Как по мне, не хватает «перетяжки» (?), когда вы доскролили до крайнего элемента. «Перетяжка» — к примеру, если видели как работает браузер в iPhone? Когда строка адреса опускается вниз за вашим движением, если дошли до края страницы. Это помогает избавится от ощущения у пользователя, что интерфейс завис.
0
simbiod #
Нет возможности быстро перейти в начало или в конец списка, что довольно часто необходимо.
+1
manny #
как раз подумал, что неплохо было бы реализовать быструю прокрутку при клике на скроллбар. Как, собственно, это привычно пользователю)
+11
Terion #
Ну ладно, статья для новичков. Ну ладно, примитивная задача.
Но, простите, для драга этой штуки подключать аж целый JQ UI — за такое руки отрывать надо.
Особенно учитывая то, что тягалка реализуется в 10 строчек кода:

$anchor.bind('mousedown', function(e){
	var startx =e.pageX;
	$(this).bind("mousemove", function(e){
		e.preventDefault();
		swipeDelta = startx - e.pageX
		// тут смещаешь то что тебе надо на swipeDelta, в данном случае позицию якоря
		startx = e.pageX;
	});
return false;
});
$(window).bind('mouseup', function(){
	$anchor.unbind("mousemove");
}


А еще лучше функцию сдвига вынести в отдельную функцию, всунуть анбинд на window внутрь бинда mousedown и биндить/анбиндить конкретную функцию.
+2
sil1999 #
Не хватает обработчика клика по дорожке скроллбара.
Кликнул по дорожке — и ползунок сразу «притянулся» к месту клика.
+2
AxelTv #
Да! Еще viventa.ru/catalog/ слева и справа от ползунка стрелки, можно их тоже сделать кликабельными
0
McBernar #
Плохо работает скролл — залипает и поведение неявное.
0
hacenator #
Напомнило как пилил галерею с «тач-скроллом аля iPhone» nail44.ru/
0
Demetros #
А нормально, что во время скролла у меня одно ядро процессора полностью загружено (FF9, Win)?
0
vitvad #
Вот скрол из тех которые сам пользовал и видел в других проектах — вполне рабочий и неплохой плагин.
Про поддержку touch — не уверен — прочем допиливается быстро.

Из минусов там — отсутствие callback'ов

ИМХО использовать jQueryUI, это лишнее — много лишнего генерится.
0
Piskov #
Простите, но как этим пользоваться на тач-устройствах? У меня вот не получается.
0
buna1991 #
Дорабатывается
0
wearymax #
С точки зрения логики: Правильно располагать ползунок не в центре, а согласно текущему положению «ленты товаров»
0
buna1991 #
Это не мое мнение, это мнение арт-директора и дизайнера.
+1
folgakauchuk #
Очень жаль, что скрипт не поддерживает настоящий горизонтальный скрол, который осуществляет обычные тачи.
+3
dyakov #
А может дизайнер хотел такой скролл?
А ползунок лишь для отображения текущего состояния (никак не должен откликаться на перетаскивание)?
0
buna1991 #
Сомневаюсь, есть несколько подобных сколов.
+6
Azy #
Руки отрывать таким дизайнерам надо, которые рисуют а не проектируют интерфейс.
Это же 100% неудобно.
1. Нельзя быстро отмотать на нужную позицию
2. Место на экране расходуется неэффективно.
3. Контрол ненативный — требует адаптации пользователя.

Посмотрите как сделано например на yoox.com и asos.com.
0
Azy #
да, выше написли — на тач устройствах будет очень неудобно
0
ainu #
И менеджерам, которые позволили заказчикам настоять на своём при разговоре с дизайнером.
0
AxelTv #
вряд ли это идея заказчика :) вероятно, здесь был полный карт бланш. печально, если дизайнер дистанцировался от проекта (если это так). проблемы решаемы, концептуально сайт очень хорош, нужен полный авторский контроль и пара итераций на доработку.
–2
buna1991 #
В данный момент идет обсуждение для реализации подобного кода, Вы же просто подбили все негативные стороны описанные в комментариях выше. Таких людей обычно называют тролями.
1. Зачем тебе мотать на нужную позицию если ты просматриваешь все товары и даже не подозреваешь что там дальше. Или у нас товары сортируются от А до Я?
2. Кхе… печальный комментарий.
3. Наверное вы просто консерватор и вам сложно адаптировавшийся к новым технологиям.
4. На нашем проекте будет разделение в функционале для touch и mouse устройств. Пока же это только версия для mouse устройств.
0
Azy #
Уважаемый, я заказываю вещи в основном через интернет и причем не на одном сайте.
Yoox, asos, kupivip, gilt и куча других. Это только по одежде не считая электроники. Я знаю о чем говорю — т.к. уже пересчитал все их грабли в плане интерфейса. Можете сказать сколько товаров вы купили в интернет магазинах за последний год? Ну или хотя бы сколько магазинов вы проанализировали чтобы прийти к такому решению?

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

2. Возможно я не верно сформулировал. Вы используете только место по горизонтали.
Рассмотрим идеальный для вас вариант — я раскрыл браузер на весь экран в мои 1920рх
i.imgur.com/GrLOF.png
У вас помещается 9 товаров. У юкса и асоса 12:
i.imgur.com/usasE.jpg
i.imgur.com/hEXOT.jpg
Но я не люблю распахнутый браузер на весь экран, потому что это неудобно и обычно он у меня где-то 50% от ширины экрана. У вас тогда вообще помещается 5.5 товаров
i.imgur.com/icX2N.png.

3. Я не консерватор, я за удобство. Мне абсолютно параллельно когда была придумана фича, если она мне упрощает жизнь. В вашем случае как минимум я не могу использовать колесо мыши. Это анноит.
4. Хорошо.

PS И не стоит считать людей глупее себя, учитывая что Вам всего 20 лет.
PSS Я надеюсь у вас не будет ужасного варианта просмотра большой фотки как на купивипе?
–1
buna1991 #
Любезный, вы вероятно всего даже не нашли наш проект, ибо в нашем проекте место уже сэкономлено, даже более того.
PS вам 27, а вы можете сказать что программированием вы занимаетесь 13 лет?
PSS А я надеюсь это не ваш проект.
0
Azy #
И что же вы такого «программировали» в 8 лет?
–1
buna1991 #
с 7ми лет вообще то. Начиналось все из решения лаб. своих друзей-студентов.
+2
Azy #
В 8 лет лабы? Интересно
Тогда мой BASIC на спектруме в таком же возрасте тоже в зачет. Итого 19 лет.
Думаю дискуссия зашла в тупик. Вы поймите одно — я не пытался троллить, или каким-то образом провоцировать вас. Ну за исключением первого комментария — слегка поддался эмоциям.
Если вы сделаете отличный интерфейс — я только за. Чем больше хороших сайтов вокруг — тем удобнее интернет для меня и тем выше уровень разработчиков с которыми я работаю. Я предпочитаю ситуацию win-win. Так что воспринимайте написанное выше исключительно в контексте фидбека. Как на него реагировать — в плане изменений решать только Вам.

Ну и если не читали — обязательно почитайте Раскина «Интерфейс» и Купера «Психбольница в руках пациентов». Удачи.
0
Azy #
И вдогонку: темный фон — плохое решение, ибо сразу видна плохая обтравка моделей и косяки со светом.
+3
mrmager #
Не работает прокрутка колесиком, а целиться мышью в эту маленькую штуку нет никакого желания.
+1
ilichme #
ставлю плюсик за трудолюбие и новаторство, но, извините, результат мне не понравился.
ползунок не только для того, чтобы скролить, но и для того, чтобы оценить, сколько ещё до конца \ как далеко от начала. а у вас абсолютно непонятно, добрались ли мы до конца или там ещё сотня элементов.
+2
yuretsz #
Без прокрутки колесом это ухудшение, а не улучшение
0
anreyyyy #
Согласен!
Какого… нету скрола колесиком??
–1
buna1991 #
Для всех вышестоящих комментариев на которые я не ответил. В статье лишь пример для размышлений, проект ещё дописывается. К тому же я пишу полноценный плагин, без jQ UI в данном на сайте сделано с ним только в целях быстрого запуска… для раскрутки и запуска… проект будет допиливатся до идеала.
0
WellWisher #
прошу прощения, подправьте комментарии в статье

//Вычеслим css left для центрального положения ползунка

Долго думал что же всё-таки раздражает глаз, потом нашел таки;)

ключевое проверочное слово — число

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.