Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zoom is unusably faster in chrome than ff #4

Open
ancms2600 opened this issue Jun 28, 2019 · 6 comments
Open

zoom is unusably faster in chrome than ff #4

ancms2600 opened this issue Jun 28, 2019 · 6 comments

Comments

@ancms2600
Copy link

ancms2600 commented Jun 28, 2019

nice lib.

here's my hack for trying to even out scroll speed between browsers when using touchpads:

@@ -945,7 +945,15 @@ EasyPZ.addMode(function (easypz) {
             var delta = eventData.event.wheelDelta ? eventData.event.wheelDelta : -1 * eventData.event.deltaY;
             var change = delta / Math.abs(delta);
             var zoomingIn = change > 0;
-            var scale = zoomingIn ? mode.settings.zoomInScaleChange : mode.settings.zoomOutScaleChange;
+            // Only Firefox seems to use the line unit (which we assume to
+            // be 25px), otherwise the delta is already measured in pixels.
+            var scale;
+            if (eventData.event.deltaMode === 1) {
+                scale = zoomingIn ? 0.92 : 1.08;
+            }
+            else {
+                scale = zoomingIn ? 0.98 : 1.02;
+            }
             var relativeScale = 1 - scale;
             var absScale = Math.abs(relativeScale) * mode.settings.momentumSpeedPercentage;
             var scaleSign = sign(relativeScale);

you can probably make it cleaner. but its normalizing the scale between browsers by checking WheelEvent.deltaMode.

see related:
weaveworks/scope#2788
https://github.com/weaveworks/scope/blob/d8ffea47815559ed908dd04f8593408ecb1e5b87/client/app/scripts/utils/zoom-utils.js
https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode

also you are doing event handling wrong.
in FF, events are emitted much less frequently than Chrome.
Chrome literally floods with events. You should be _.debounce()ing them.
see also:
https://stackoverflow.com/a/25991510
http://demo.nimius.net/debounce_throttle/

also, you should not be doing the work inside the event callback. the callback should just be setting a few calculation values like delta, and you should have a separate function doing the work at a controlled (probably 12-24fps) frame rate (using requestAnimationFrame(), or setTimeout if you are a savage).

sorry i don't have time to make a PR. hope this helps :)

@ancms2600 ancms2600 changed the title zoom faster in chrome than ff zoom is unusably faster in chrome than ff Jun 28, 2019
@ancms2600
Copy link
Author

ancms2600 commented Jun 28, 2019

better version

            var delta = eventData.event.wheelDelta ? eventData.event.wheelDelta : -1 * eventData.event.deltaY;
            // Only Firefox seems to use the line unit (which we assume to
            // be 25px), otherwise the delta is already measured in pixels.
            var scale = 1 + (-1 * ((delta * (eventData.event.deltaMode === 1 ? 25 : 1)) / 1000));

@ancms2600
Copy link
Author

ancms2600 commented Jun 30, 2019

implemented as Mithril.js component:

Components.PanZoom = {
    LEFT_CLICK: 0,
    MIDDLE_CLICK: 1,
    RIGHT_CLICK: 2,

    oninit(v) {
        v.state.pointer = {};
        v.state.translateX = 0;
        v.state.translateY = 0;
        v.state.scale = 1.0;
    },

    oncreate(v) {
        const fn = Components.PanZoom.handleEvent.bind(null,v);
        v.dom.addEventListener('mousemove',   fn, { passive: true });
        v.dom.addEventListener('mousedown',   fn);
        v.dom.addEventListener('mouseup',     fn, { capture: true });
        v.dom.addEventListener('wheel',       fn, { capture: true });
        v.dom.addEventListener('contextmenu', fn);
        v.dom.addEventListener('touchmove',   fn);
        v.dom.addEventListener('touchstart',  fn);
        v.dom.addEventListener('touchend',    fn, { passive: true });
    },

    handleEvent(v,e) {
        if (true === v.attrs.debug && 'mousemove' !== e.type && 'touchmove' !== e.type) {
            console.debug(`PanZoom debug ${e.type}`, e);
        }
        if ('mousemove' === e.type || 'mousedown' === e.type || 'touchmove' === e.type) {
            let x,y;
            if ('touchmove' === e.type) {
                x = e.changedTouches[0].clientX;
                y = e.changedTouches[0].clientY;
                // prevent page drag
                e.preventDefault();
            }
            else {
                x = e.clientX;
                y = e.clientY;
            }
            v.state.pointer.lastX = v.state.pointer.x;
            v.state.pointer.lastY = v.state.pointer.y;
            v.state.pointer.x = x;
            v.state.pointer.y = y;
            v.state.pointer.deltaX = v.state.pointer.x - v.state.pointer.lastX;
            v.state.pointer.deltaY = v.state.pointer.y - v.state.pointer.lastY;
            if (null != v.state.pointer.panningBy) {
                v.state.translateX += (v.state.pointer.deltaX || 0);
                v.state.translateY += (v.state.pointer.deltaY || 0);
                Components.PanZoom.update(v);
            }
            else if (true === v.attrs.debug) {
                m.redraw();
            }
        }
        if (('mousedown' === e.type || 'touchstart' === e.type) && v.dom === e.target) {
            v.state.pointer.button = e.button;
            if (Components.PanZoom.LEFT_CLICK === e.button || Components.PanZoom.MIDDLE_CLICK === e.button || 'touchstart' === e.type) {
                v.state.pointer.panningBy  = e.type;
                let x,y;
                if ('touchstart' === e.type) {
                    x = e.changedTouches[0].clientX;
                    y = e.changedTouches[0].clientY;
                }
                else {
                    x = e.clientX;
                    y = e.clientY;
                    // prevent text selection
                    e.preventDefault();
                }
                v.state.pointer.lastX = x;
                v.state.pointer.lastY = y;
                v.state.pointer.x = x;
                v.state.pointer.y = y;
                v.state.pointer.deltaX = 0;
                v.state.pointer.deltaY = 0;
                Components.PanZoom.update(v);
            }
        }
        else if ('mouseup' === e.type || 'touchend' === e.type) {
            if ('mouseup'  === e.type) {
                v.state.pointer.button = undefined;
            }
            if (
                ('mouseup'  === e.type && 'mousedown'  === v.state.pointer.panningBy) ||
                ('touchend' === e.type && 'touchstart' === v.state.pointer.panningBy)
            ) {
                v.state.pointer.panningBy  = undefined;
            }
        }
        else if ('wheel' === e.type) {
            // prevent page scroll
            e.preventDefault();
            v.state.scale = Utils.clamp(v.state.scale + (
                (
                    (e.wheelDelta ? e.wheelDelta : -e.deltaY) *
                    // Only Firefox seems to use the line unit (which we assume to be 25px),
                    // otherwise the delta is already measured in pixels.
                    (e.deltaMode === 1 ? 25 : 1)
                ) / 1000
            ), 0.2, 2);
            Components.PanZoom.update(v);
        }
        else if ('contextmenu' === e.type && v.dom === e.target) {
            // disable right-click menu
            e.preventDefault();
        }
    },

    update(v) {
        v.state.lens.dom.style.transform =
            `matrix(${v.state.scale}, 0, 0, ${v.state.scale}, ${v.state.translateX}, ${v.state.translateY})`;
    },

    view(v) {
        return m('.pan-zoom-container', v.state.lens = m('.pan-zoom-lens',
            (true === v.attrs.debug &&
                m('pre.debug', JSON.stringify(_.pick(v.state, ['pointer','translateX','translateY','scale']), null, 2))),
            v.children));
    },
};

@michaschwab
Copy link
Owner

Thank you, this looks promising! I will take a look next week.

@michaschwab
Copy link
Owner

If you'd like to see changes regarding the event handling and timing, feel free to create a separate issue for that. I am well aware of issues when heavy work is put on events rather than animation frames, but I'm not sure that's the case here so for the time being I'm ok with the event handling as-is.

I've played with deltaMode a little and it's not entirely clear to me when the delta is supposed to be different. I've been able to reproduce the faster zoom on Firefox, but only when using a track pad to zoom, and not a scroll wheel. Is this the use case? A more concise example of the problem, e.g. on jsfiddle, would make your point much more understandable.

@ancms2600
Copy link
Author

Yes correct. Different input devices have different scale resolutions in different browsers. This is by design so any software not accounting for it is going to provide an experience that is not consistent for all users.

It's not well documented but some googling regarding the purpose of WheelEvent.deltaMode may help. Also see my prior links to PRs in other projects which were eventually forced to deal with this same issue.

@mcdemarco
Copy link

It was also unusable for me on a trackpad in Safari without the fix mentioned above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants