Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery mousemove performance - throttle events?

We are facing a problem with jQuery event propagation performance connected to mousemove:

We have a screen filling canvas and need to track if a user drags the mouse on it, so we have added a mouse move listener on that object like this:

ourCanvas.on('mousemove',
   function(event) {
      event.preventDefault();
      //our drag code here
   }
});

This code works fine, but we had severe performance issues in the current Firefox (24) on one test system. The profiler tells us, that most of the time was spent in jQuery.event.dispatch() (we tried current latest jQuery 1.8, 1.9, 1.10 and 2.0).

We successfully decreased the time spent in the dispatch() function by using the "jQuery.event.fix()" performance optimization here: http://bitovi.com/blog/2012/04/faster-jquery-event-fix.html but performance on that test system was still way below what we expected.

After some further testing, I managed to pin this down on the mouse used on the system: It used 1000Hz. We switched the used mouse down to 125Hz and voila, the performance was great.

Our assumption was, that the high Hz rate on the mouse caused a lot of mousemove events, so we changed the above code to apply an event throttle and only call the event handling every X milliseconds:

var lastMove = 0;
var eventThrottle = 1;
ourCanvas.on('mousemove',
   function(event) {
      event.preventDefault();
      var now = Date.now();
      if (now > lastMove + eventThrottle) {
          lastMove = now;
          //our drag code here
      }
   }
});

And it worked like a charme, the performance was great. Even though we only skip two milliseconds of events.

Now I have two questions:

  1. We have other locations where we attach mousemove listeners to different HTML elements and I'd like to add this hand-made throttle to all of those mousemove handlers to not run into the problem again. Is this somehow possible to do in a nice way in jQuery (2.0.3)? I've seen preDispatch hooks in the jQuery javascript but they are already after the call to fix() which also uses some time and I'd like to save that call as well.

  2. I was puzzled by the fact that already an eventThrottle of 2ms was sufficient to get really good performance so I added a counter to see how many events are being skipped. The surprising result: it skips only 0-1 events... With a throttle of 100ms, the skipped events were in the order of 60-70, so if there is less than 1 mousemove event per ms, why does this code have such a positive effect after all?

Thanks for any comments, Christopher

like image 606
Christopher Lörken Avatar asked Oct 09 '13 16:10

Christopher Lörken


2 Answers

1 - there is a throttle jQuery plugin : https://github.com/cowboy/jquery-throttle-debounce

As you can read in the examples, you can replace :

// Bind the not-at-all throttled handler to the resize event.
$(window).resize( handler );

// Bind the throttled handler to the resize event.
$(window).resize( $.throttle( 250, handler ) ); // This is the line you want!

2 - do you want to post your handler's code ?

One blind suggestion : Firebug has performance issues with FF 24. Have you tried to compare performances with Firebug enabled/disabled ?

like image 22
LeGEC Avatar answered Nov 10 '22 15:11

LeGEC


In late 2015 I ran into what I eventually discovered was this problem.

In my browser app I paint multiple circles of various sizes at particular locations, then I drag the visible portion of the total screen, revealing only the circles from the full background that are visible at the current zoom level. Dragging the mouse generates a mousemove event, which triggers a call to my render routine, which in turn causes a repaint for each visible circle.

In testing this in IE 11, I found that once I had around 100+ circles in the viewable area, rendering while dragging the mouse became extremely choppy. The profiler indicated this was almost entirely due to the paint() routine.

My code was already using requestAnimationFrame() from a library. Interestingly, while dragging the screen, I saw the slowdown; but if I only dragged the screen and released it, letting the library code continue to animate the movement with deceleration, repainting was smooth as butter. The slowdown occurred only while dragging the mouse. The problem definitely seemed to be with mousemove. (Back to this in a moment.)

I pared down the paint() routine to nothing but a simple filled arc -- same problem. I tried painting the filled circle to an off-screen canvas whenever I changed the zoom level, then using drawImage() to copy the offscreen canvas to my main screen -- this improved performance, but it was still unusably choppy in IE. I then tried using this technique to paint all the circles to an offscreen canvas the same size as my main visible window, then changing paint() to do nothing but copy the offscreen canvas to my visible canvas -- this again gave a small improvement, but not enough.

I then tried running my app in various browsers:

IE 11: very choppy Firefox 42: very choppy Chrome 47: perfectly smooth at all zoom levels Opera 34: perfectly smooth at all zoom levels Desktop Safari 5.1.7 (on PC): slightly choppy at all zoom levels

The problem was definitely related to mousemove and to how it's handled by different browsers.

Eventually I found this question on StackOverflow and its suggestion that the mouse itself is sending so many mousemove events that it's swamping the ability of the browser to repaint quickly enough. And I do have a modern mouse with a high event generation rate.

I tried adding the eventThrottle check to my mousemove event handler, and voila! Success. My code now renders smoothly on all browsers. (Upvoted with pleasure. :) )

I wanted to add this additional information for anyone who might be encountering the problem of poor paint() performance in IE and Firefox while dragging with a high-frequency mouse. The proposed solution to throttle mousemove events worked for me.

like image 153
Bart Stewart Avatar answered Nov 10 '22 16:11

Bart Stewart