OSX inertia scrolling causing mousewheel.js to reg

2019-03-30 11:39发布

问题:

I have a pretty standard Flexslider instance I'm working on. Flexslider incorporates well with the jquery.mousewheel.js plugin, allowing slides to move on mousewheel events.

However, Mac OSX and others employ 'intertia' to scrolling effects, making it virtually impossible to scroll only one slide left or right. The general effect is that even the slightest scroll motion in these situations triggers multiple mousewheel events and scrolls the slider multiple images left or right. Obviously, this can't happen!

I think the solution is to use underscore.js - specifically the 'debounce' function. However, I'm relatively inexperienced in JS / jQuery and can't figure out how to implement it.

From underscore's documentation:

Debounce: _.debounce(function, wait, [immediate])

Creates and returns a new debounced version of the passed function which will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked. Useful for implementing behavior that should only happen after the input has stopped arriving. For example: rendering a preview of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on.

Pass true for the immediate parameter to cause debounce to trigger the function on the leading instead of the trailing edge of the wait interval. Useful in circumstances like preventing accidental double-clicks on a "submit" button from firing a second time.

var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);

Here's the code I'm using to initialize flexslider:

// MOUSEWHEEL FIX:

function debounceEffect() { // apply debounce to mousewheel here

}

// Initialize FlexSlider
$(window).load(function() {     
    $('div.flexslider').flexslider({
        animation:'slide',
        controlNav:true,
        mousewheel:true,
        pauseOnHover:true,
        direction:'horizontal',
        animationSpeed:500,
        slideshowSpeed:5000,
        after:debounceEffect
    });
});

Unfortunately, I can't figure out how to apply the debounce function to the mousewheel event. I think I just don't understand the moving parts well enough.

Here is the relevant bit of code from the flexslider plugin:

    // MOUSEWHEEL:
    if (vars.mousewheel) {
      slider.bind('mousewheel', function(event, delta, deltaX, deltaY) {
        event.preventDefault();
        var target = (delta < 0) ? slider.getTarget('next') : slider.getTarget('prev');
        slider.flexAnimate(target, vars.pauseOnAction);
      });
    }

I can edit this to include some of the mousewheel plugin code as well if that's important, or you could check it out on github.

I would appreciate any help on this!

回答1:

I think the main problem is that you won't be able to apply debouncing as an after effect, or at least I wouldn't know how. Let's talk through how the pieces work:

When you call $('div.flexslider').flexslider({ .. }, jQuery finds all the elements like div.flexslider (as usual) and then adds a flexslider to that. flexslider adds all the machinery to make it a slider, including setting up bind events. It's the actual bind events themselves that you need to debounce.

Seems like you got pretty close by finding the relevant flexslider bind code. I think what you'd want ideally is this:

if (vars.mousewheel) {
  slider.bind('mouse wheel', _.debounce(function(…) {
   // usual flex slider code.
  }), 300);
}

I'm not sure how you'd actually inject this debounce without directly editing the flexslider code.

You probably could debounce the mouse wheel plugin itself, if you want to globally affect mousewheel events (which I guess you might). I think you'd have to wrap the handler in a debounce, and then pass the debounced-handler to the addEventListener and removeEventListener.



回答2:

Looks like this problem still exists.

I've got some good results using this debounce plugin and changing flexslider.js so it's the folllowing:

slider.bind('mousewheel', $.debounce( 100, true, function(event, delta, deltaX, deltaY){
     event.preventDefault();
     var target = (delta < 0) ? slider.getTarget('next') : slider.getTarget('prev');

    slider.flexAnimate(target, slider.vars.pauseOnAction);
}));