Wheel-indicator — плагін емуляції touchstart при роботі з трекпадами

Інерційні пристрої введення — це такі пристрої, як тачскріни, трекпеда, magic mouse тощо, По своїй роботі трекпеда та magic mouse нагадують тачскріни мобільних пристроїв, тобто продовжують генерувати події мышинного колеса після того, як користувач закінчив жест. Але на відміну від них у нас відсутня нативне подія touchstart. Все, що ми маємо, це об'єкт події wheel. Touchstart часто буває необхідний, щоб комфортно реалізувати роботу так званих fullpage-сайтів, де при скролінгу відбувається перехід між екранами. Прикладом такого сайту може послужити <a href=«alfabank».ru/">alfabank " . На ньому присутня проблема прокрутки двох екранів поспіль при використанні magic mouse або трекпеда (особливо на макбуках). Досить слабкий жест скролінгу вниз прокручує до другого, а потім відразу до третього екрану. Щоб потрапити на другий екран, доводиться користуватися скроллбаром. Саме такого роду проблеми ми спробували вирішити використовуючи лише об'єкт події wheel.

Отже, як же реалізувати touchstart?
Почнемо з самого просто і будемо рухатися по наростаючій. Найпростіший варіант реалізації touchstart:
0. Тиша.
1. Початок генеруватися подія wheel. Це і є наш touchstart
2. Припустимо, що між подіями ніяк не може бути більше 200мс (насправді від 10 до 30 мс), тому просто через 200 мс після останнього події знову генеруємо touchstart при появі нових об'єктів подій.

Вже непогано, дві сторінки точно не проскролятся за один рух. Але є суттєва проблема: спроба почати нову ітерацію скролінгу до кінця поточної призведе тільки до подовження поточної.

Щоб вирішити цю проблему, необхідно зрозуміти момент, коли користувач зробив новий жест до закінчення роботи попередньої ітерації.
Проаналізувавши об'єкт події wheel, ми відразу звернули увагу на полі deltaY. Воно відображає силу, з якою в поточний момент часу працює пристрій.
Якщо відобразити значення deltaY на графіку, то жест буде мати приблизно такий вигляд:

А це те, що нам треба відловити.

Таким чином задача зводиться до того, щоб порівнювати попереднє значення deltaY з поточним. І якщо воно більше, значить користувач почав нове усвідомлене рух, тобто новий touchstart стався.

Начебто все відмінно, інтерфейс став більш чуйний, стало можна робити будь-яку кількість жестів поспіль, не чекаючи закінчення роботи попередній ітерації. Але на практиці алгоритм давав збій: touchstart часто генерувався частіше необхідного. Іноді по 2-3 рази за одну ітерацію. Чому так відбувалося? Аналізі числового ряду deltaY з однієї ітерації показав, що іноді незважаючи на спад ітерації (тобто уповільнення роботи інерції, вираженим у все менших значеннях deltaY в кожному наступному подію wheel) іноді поточний deltaY може бути дорівнює або більше попереднього. Причому іноді це відбувалося через раз:
21, 17, 15, 18, 12, 14, 10, 7…

або два поспіль збільшення
21, 17, 15, 18, 19, 14, 10…

Численні досліди показали, що таких ситуацій майже не буває більше трьох поспіль для обох випадків. Вносимо корективи в алгоритм: тепер touchstart генерується тільки якщо поточний deltaY більше попереднього і наступного deltaY більше поточного. Тепер все працює непогано, і явних проблем більше немає.

Взявши за основу цей підхід, ми написали плагін wheel-indicator. В аналізі також використовуються інші чинники, але описувати їх всі у рамках цієї статті немає сенсу.

Звичайні мишки
Плагін також можна використовувати, як легку заміну відомому jquery-mousewheel у випадках, коли вам потрібно тільки кроссбраузерно визначити напрям роботи коліщатка. Якщо мишка користувача триггерит черезмерно багато подій, плагін також буде це нормалізувати. Іноді це буває корисно, наприклад, скролл колесом таку карусель не дуже комфортно. Крім цього на основі плагіна можна реалізовувати неблокирующие інтерфейси. Наприклад, тут можна скролл в процесі анімації в будь-яку сторону, і кількість переходів буде одно кол-ву жестів користувача.

Тестуванняоскільки цей алгоритм не остаточний і можливо буде поліпшуватися, захотілося мати можливість його тестувати. По суті на вхід плагін приймає числовий ряд з deltaY і аналізує його. А значить для написання тестів досить nodejs і travis-ci.org для тестування комітів.
Щоб мати можливість тестувати плагін в nodejs, необхідно, щоб він міг експортувати себе в форматі commonjs.
Для цього додаємо перевірку і експорт констурктора:
if (typeof exports === 'object') {
module.exports = WheelIndicator;
}

Вхідні дані для плагіна надходять через об'єкт події після створення обробника за допомогою addEventListener… Таким чином, у тестах нам необхідно «замокати» цей метод:
global.document = {
addEventListener.: function(type, handler){
currentDeltaArr.forEach(function(delta){
handler({
deltaY: delta
});
});
}
};

, де delta — це масив тестового числового ряду з deltaY. Для зручного отримання таких рядів з різних пристроїв і ОС ми зробили тестовий стенд.

Ось власне і все, тепер тільки залишається зареквайрить плагін, створити інстанси і звірити отримані від плагіна дані з еталонними.

Приклад вхідних даних для тіста:
down: {
moves: [ 'down' ],
delta: [1,4,12,32,55,69,154,156,158,148,137,130,122,116,111,108,103,97,93,88,84,80,74,71,65,61,57,54,50,46,42,39,36,33,31,27,25,23,21,18,17,15,14,13,12,11,9,8,8,7,6,6,14,4,4,3,3,3,2,2,4,1,2,1,1,1,1,1,1,1,1,1,1],
device: 'Mac OSX notebook trackpad'
}


Почитати про підключення, документацію і завантажити плагін ви можете сторінці репозиторію.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.