Javascript scrollbar class and mousewheel speed in

2019-05-11 13:13发布

I'm getting many reports that the mousewheel behaves differently in different browsers when using this scrollbar class. In some browsers (like Firefox) it's extremely slow while in others (mostly newer versions of Safari on Snow Leopard) it's perfect.

Any ideas what's going on here and how to fix it? I'm using the Mootools library. One line to pay attention to here is the wheel: (Browser.firefox) ? 20 : 1 line. This is where you set the speed or steps for the mousewheel.

Here it is set up in a jsFiddle: http://jsfiddle.net/brandondurham/6SUyM/

var ScrollBar = new Class({

    Implements: [Events, Options],

    options: {
        wheel: (Browser.firefox) ? 20 : 1
    },

    initialize: function(main, options) {

        this.setOptions(options);

        this.main = $(main);
        this.content = this.main.getFirst();

        this.vScrollbar = new Element('div', {
            'class': 'scrollbar'
        }).inject(this.content, 'after');

        this.vTrack = new Element('div', {
            'class': 'track'
        }).inject(this.vScrollbar);

        this.vThumb = new Element('div', {
            'class': 'handle'
        }).inject(this.vTrack);

        this.bound = {
            'vStart': this.vStart.bind(this),
            'end': this.end.bind(this),
            'vDrag': this.vDrag.bind(this),
            'vTouchDrag': this.vTouchDrag.bind(this),
            'wheel': this.wheel.bind(this),
            'vPage': this.vPage.bind(this),
        };

        this.vScrollbar.set('tween', {
            duration: 200,
            transition: 'cubic:out'
        });
        this.main.addEvent('mouseenter', function(event){
            this.vScrollbar.get('tween').cancel();
            this.vScrollbar.tween('width', 12);
        }.bind(this));
        this.main.addEvent('mouseleave', function(event){
            this.vScrollbar.get('tween').cancel();
            this.vScrollbar.tween('width', 0);
        }.bind(this));

        this.vPosition = {};
        this.vMouse = {};
        this.update();
        this.attach();

        this.scrollContent = new Fx.Scroll(this.content, {
            duration: 800,
            transition: Fx.Transitions.Cubic.easeOut,
        });
        this.scrollThumb = new Fx.Morph(this.vThumb, {
            duration: 400,
            transition: Fx.Transitions.Cubic.easeOut,
        });
    },

    update: function() {

        var panel_id = (this.content.getFirst()) ? this.content.getFirst().get('id') : '';

        if ((this.content.scrollHeight <= this.main.offsetHeight) || panel_id == 'random-doodle') this.main.addClass('noscroll');
        else this.main.removeClass('noscroll');

        this.vContentSize = this.content.offsetHeight;
        this.vContentScrollSize = this.content.scrollHeight;
        this.vTrackSize = this.vTrack.offsetHeight;

        this.vContentRatio = this.vContentSize / this.vContentScrollSize;

        this.vThumbSize = (this.vTrackSize * this.vContentRatio).limit(12, this.vTrackSize);

        this.vScrollRatio = this.vContentScrollSize / this.vTrackSize;

        this.vThumb.setStyle('height', this.vThumbSize);

        this.vUpdateThumbFromContentScroll();
        this.vUpdateContentFromThumbPosition();

    },

    vUpdateContentFromThumbPosition: function() {
        this.content.scrollTop = this.vPosition.now * this.vScrollRatio;
    },

    vUpdateContentFromThumbPosition2: function() {
        var pos = this.vPosition.now * this.vScrollRatio;
        this.scrollContent.start(0, pos);
    },

    vUpdateThumbFromContentScroll: function() {
        this.vPosition.now = (this.content.scrollTop / this.vScrollRatio).limit(0, (this.vTrackSize - this.vThumbSize));
        this.vThumb.setStyle('top', this.vPosition.now);
    },

    vUpdateThumbFromContentScroll2: function(pos) {
        this.vPosition.now = (this.content.scrollTopNew / this.vScrollRatio).limit(0, (this.vTrackSize - this.vThumbSize));           
        this.scrollThumb.start({
            'top': this.vPosition.now       
        });
    },

    attach: function() {
        if (this.options.wheel) this.content.addEvent('mousewheel', this.bound.wheel);
        this.content.addEvent('touchstart', this.bound.vStart);
        this.vThumb.addEvent('mousedown', this.bound.vStart);
        this.vTrack.addEvent('mouseup', this.bound.vPage);
    },

    wheel: function(event) {
        this.content.scrollTop -= event.wheel * this.options.wheel;
        this.vUpdateThumbFromContentScroll();
        event.stop();
    },

    scrollTo: function(pos){
        myInstance = this;
        this.content.scrollTopNew = pos;
        this.scrollContent.start(0, this.content.scrollTopNew);
        myInstance.vUpdateThumbFromContentScroll2(pos);
    },

    vPage: function(event) {
        // if scrolling up
        if (event.page.y > this.vThumb.getPosition().y) {
            myInstance = this;
            this.content.scrollTopNew = this.content.scrollTop.toInt() + this.content.offsetHeight.toInt();
            this.scrollContent.start(0, this.content.scrollTopNew);
        }
        // if scrolling down
        else {
            myInstance = this;    
            this.content.scrollTopNew = this.content.scrollTop.toInt() - this.content.offsetHeight.toInt();    
            this.scrollContent.start(0, this.content.scrollTopNew);       
        }
        myInstance.vUpdateThumbFromContentScroll2(event.page.y);
        event.stop();
    },

    vStart: function(event) {
        this.vMouse.start = event.page.y;
        this.vPosition.start = this.vThumb.getStyle('top').toInt();
        document.addEvent('touchmove', this.bound.vTouchDrag);
        document.addEvent('touchend', this.bound.end);
        document.addEvent('mousemove', this.bound.vDrag);
        document.addEvent('mouseup', this.bound.end);
        this.vThumb.addEvent('mouseup', this.bound.end);
        event.stop();
    },

    end: function(event) {
        document.removeEvent('touchmove', this.bound.vTouchDrag);
        document.removeEvent('mousemove', this.bound.vDrag);
        document.removeEvent('mouseup', this.bound.end);
        this.vThumb.removeEvent('mouseup', this.bound.end);
        event.stop();
    },

    vTouchDrag: function(event) {
        this.vMouse.now = event.page.y;
        this.vPosition.now = (this.vPosition.start - (this.vMouse.now - this.vMouse.start)).limit(0, (this.vTrackSize - this.vThumbSize));
        this.vUpdateContentFromThumbPosition();
        this.vUpdateThumbFromContentScroll();
        event.stop();
    },

    vDrag: function(event) {
        this.vMouse.now = event.page.y;
        this.vPosition.now = (this.vPosition.start + (this.vMouse.now - this.vMouse.start)).limit(0, (this.vTrackSize - this.vThumbSize));
        this.vUpdateContentFromThumbPosition();
        this.vUpdateThumbFromContentScroll();
        event.stop();
    }

});

1条回答
地球回转人心会变
2楼-- · 2019-05-11 13:24

The mouse wheel event is very dodgy in javascript, main issue being usually Safari as they used to adjust the ratio on every point release, and even then the values reported by the event are not the same in all major browsers.

There has been some discussion about this on the MooTools tracker some time ago (link) and comparing different solutions I concluded that there's no standard way to normalize the event.
The last message on that issue shows a possible solution for normalizing (link), but it breaks wheel acceleration in Safari (and probably any other acceleration that other Browser/OS/Mouse Driver combination offers) so it is a tradeoff that you will have to evaluate if it fits to the requirements of your usage scenario.

查看更多
登录 后发表回答