📜 ⬆️ ⬇️

Wheel-indicator - a touchstart emulation plugin when working with trackpads

Inertial input devices are such devices as touchscreens, trackpads, magic mouse, etc. In their work, trackpads and magic mouse resemble mobile devices touchscreens, i.e. continue to generate mouse wheel events after the user has finished the gesture. But unlike these, we have no native touchstart event. All we have is a wheel event object. Touchstart is often necessary to comfortably implement the work of the so-called fullpage sites, where when scrolling a transition occurs between the screens. An example of such a site is alfabank . It also has the problem of scrolling two screens in a row when using a magic mouse or a trackpad (especially on MacBooks). A rather weak scrolling gesture scrolls down to the second, and then immediately to the third screen. To get to the second screen, you have to use the scrollbar. We tried to solve exactly this kind of problem using only the event object wheel .

So, how do you implement touchstart?
Let's start with the most simple and we will move on increasing. The simplest way to implement touchstart is:
0. Silence.
1. The start event wheel. This is our touchstart.
2. Suppose that between events there can be no more than 200 ms (actually from 10 to 30 ms), therefore, just 200 ms after the last event, we again generate a touchstart when new event objects appear.

Already not bad, two pages are definitely not slipping in one movement. But there is a significant problem: an attempt to start a new iteration of scrolling to the end of the current one will only lead to lengthening the current one.

To solve this problem, you need to understand the moment when the user made a new gesture before the end of the previous iteration.
After analyzing the wheel event object, we immediately noticed the deltaY field. It reflects the strength with which the device is currently operating.
If you display the deltaY values ​​on the graph, the gesture will look something like this:

And this is what we need to catch.

Thus, the task is reduced to comparing the previous deltaY value with the current one. And if it is more, then the user has started a new conscious movement, that is, a new touchstart has occurred.
')
It seems that everything is fine, the interface has become more responsive, it has become possible to make any number of gestures in a row without waiting for the previous iteration to finish. But in practice, the algorithm failed: touchstart was often generated more often than necessary. Sometimes 2-3 times per iteration. Why did this happen? Analysis of the numerical series deltaY from one iteration showed that sometimes, despite the iteration decline (that is, slowing down the inertia, expressed in less and less deltaY values ​​in each subsequent wheel event), the current deltaY can sometimes be equal to or greater than the previous one. And sometimes it happened every other time:
21, 17, 15, 18 , 12, 14 , 10, 7 ...

either two consecutive increases
21, 17, 15, 18 , 19 , 14, 10 ...

Numerous experiments have shown that such situations practically do not happen more than three in a row for both cases. We make adjustments to the algorithm: now the touchstart is generated only if the current deltaY is greater than the previous one and the next deltaY is greater than the current one. Now everything works well, and there are no more obvious problems.

Taking this approach as a basis, we wrote a wheel-indicator plugin. Other factors are also used in the analysis, but there is no point in describing them all within the framework of this article.

Ordinary mouse

The plugin can also be used as an easy replacement for the well - known jquery-mousewheel in cases where you only need to cross-browser to determine the direction of the wheel. If the user's mouse triggers too many events, the plugin will also normalize it. Sometimes it is useful, for example, to scroll the wheel such a carousel is not very comfortable. In addition, non-blocking interfaces can be implemented based on the plugin. For example, here you can scroll in the process of animation in any direction, and the number of transitions will be equal to the number of user's gestures.

Testing
Since This algorithm is not final and it will probably be improved, I wanted to be able to test it. In fact, the plugin accepts a numeric row from deltaY as input and analyzes it. So for writing tests, nodejs and travis-ci.org are enough to test commits.
To be able to test the plugin in nodejs, it is necessary that it can export itself in the commonjs format.
To do this, add verification and export of the constructor:
if (typeof exports === 'object') { module.exports = WheelIndicator; } 

The input to the plug-in comes through an event object after the handler is created using addEventListener. Thus, in tests, we need to “lock” this method:
 global.document = { addEventListener: function(type, handler){ currentDeltaArr.forEach(function(delta){ handler({ deltaY: delta }); }); } }; 

where delta is an array of test number series from deltaY. For convenient obtaining of such rows from various devices and OS we made a test bench .

That's all, now it only remains to restore the plugin, create an instance and verify the data from the plugin with the reference.

Sample incoming data for test:
 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' } 


You can read about the connection, documentation and download the plugin on the repository page .

Source: https://habr.com/ru/post/260829/


All Articles