w3c / uievents

UI Events
https://w3c.github.io/uievents/
Other
145 stars 52 forks source link

Needs alternative WheelEvent.deltaMode, WheelEvent.delta(X|Y|Z) #181

Open masayuki-nakano opened 6 years ago

masayuki-nakano commented 6 years ago

Currently, unit of WheelEvent.delta(X|Y|Z) value is defined by WheelEvent.deltaMode in UI Events. However, Chrome and Edge almost always fire wheel events with WheelEvent.DOM_DELTA_PIXEL. Therefore, a lot of web sites are broken on Gecko which is the only browser firing wheel events with native event's units.

So, for backward compatibility, WheelEvent.deltaMode should return always WheelEvent.DOM_DELTA_PIXEL, unfortunately. However, some better web application developers may want raw delta(X|Y|Z) values as far as possible.

Therefore, I'd like to propose that UI Events should declare that WheelEvent.delatMode always return WheelEvent.DOM_DELTA_PIXEL and WheelEvent.delta(X|Y|Z) are delta values in pixels. Additionally, there should be alternative attributes which are set raw event information. E.g., WheelEvent.rawDelta(Mode|X|Y|Z).

masayuki-nakano commented 6 years ago

Bug of Firefox is here: https://bugzilla.mozilla.org/show_bug.cgi?id=1392460

If it'd be possible, there should've been:

masayuki-nakano commented 6 years ago

@garykac Could you take a look? This is really annoying Spec issue. (Perhaps, some Chrome for Windows users have same issue as users of Firefox for Windows or Linux users since Chrome uses DOM_DELTA_PAGE if user sets per-page-scroll in the system settings.)

garykac commented 6 years ago

To summarize the proposal:

Because many web developers don't respect the deltaMode setting and simply assume that delta* always returns pixel values, we're proposing changing delta* to always return pixel values.

This change won't affect sites that properly check the deltaMode attribute, but it would break any site that assumes Firefox == DOM_PIXEL_LINE without actually checking deltaMode.

Sites that currently take advantage of Firefox's non-pixel deltas will no longer do so until they switch to use the new attributes on the event.

masayuki-nakano commented 6 years ago

FYI: As far as I've tested, deltaMode value of each browser:

garykac commented 6 years ago

@rbyers @travisleithead Any thoughts on this proposal?

RByers commented 6 years ago

I've seen this difference between Chrome and Firefox cause real compat problems on real sites, so I support trying to fix it. I agree that delta is so often pixel that it's safer to require that it ALWAYS be pixel.

So the proposal seems reasonable to me, though I worry we might just have the same problem with rawDeltaMode. It seems to me having any property whose units vary based on another property is potentially problematic. Why not have linesDeltaX/Y and pageDeltaX/Y values (which could be undefined)? The Z axis almost certainly doesn't make sense for lines/pages, right? Does the X axis have a sensible and useful definition for lines?

Anyway @NavidZ is the Chrome expert here, so this is just my $0.02 - I don't have objections either way.

wisniewskit commented 5 years ago

Let's try to get some momentum going on this one again :)

@NavidZ, mind chiming in with your thoughts?

@masayuki-nakano, what do you think about the concern @RByers raised?

For what it's worth, I feel Rick has a good point and suggestion here. I feel that the default behavior should be consistent, as in my experience working on webcompat, web pages almost always presume pixel values and aren't even aware that there are other potential units. I feel that making the existing property a legacy (and pixel-only) value would be best, along with new properties that are specific to the units.

In fact I wonder if there is even a need to have "pages" and "lines" values at all (as well as a variable default unit)? I also wonder if they aren't a potential fingerprinting vector in some way?

masayuki-nakano commented 4 years ago

(Sorry, I didn't realize the mention here.)

Yes, sounds like that's great idea. I agree with it.

However, I have a concern about implementing it. For making it, each implementation should have computed value of lineDeltaX/Y and pageDeltaX/Y or line height or page height/width for computing them later since events may be stored with variables and may be referred later (i.e., after ending the propagation).

I don't mind the cost as a developer of Mozilla DOM Events, but I'm not sure how other browser developers.

And in my understanding, the line height, page width and page height values are used the value at dispatching wheel event rather than the attributes are accessed. So, I hope that UI Events should define:

  1. When these reference values are fixed.
  2. Which element's size should be referred (Event.target? Nearest ancestor scrollable Element? Root element?).
CWies commented 4 years ago

@masayuki-nakano could you add the unit in which the values(deltaX/Y/Z) are actually returned to your list above? I found that, on Windows, when set to multiple line scroll, the actual unit is always lines, and when set to pages it is always pages. Though I have neither a macOS nor a Linux device. Are they always returned in lines? I am especially wondering about macOS with trackpad, since firefox also uses pixels there.

masayuki-nakano commented 4 years ago

@CWies Isn't you asking about what I commented in https://github.com/w3c/uievents/issues/181#issuecomment-392648065 ?

CWies commented 4 years ago

@masayuki-nakano As I understand, thats what deltaMode returns in each browser. Though I need to know in which unit deltaX/Y/Z are actually returned, because in my testing, both edge and chrome, while returning DOM_DELTA_PIXEL for deltaMode, only give values of 1 for deltaX/Y/Z every time i turn the scrollwheel to the next step, which is actually 1 Line and not 1 Pixel(as far as i understand)

masayuki-nakano commented 4 years ago

I need to know in which unit deltaX/Y/Z are actually returned

The value can be checked with deletaMode as you know.

because in my testing, both edge and chrome, while returning DOM_DELTA_PIXEL for deltaMode, only give values of 1 for deltaX/Y/Z every time i turn the scrollwheel to the next step, which is actually 1 Line and not 1 Pixel

Don't you see a lot of wheel events in such case? If so, the device tries to scroll smoothly with splitting one action to multiple wheel events.

CWies commented 4 years ago

Don't you see a lot of wheel events in such case?

No... both on edge and chrome not

The value can be checked with deletaMode as you know.

Why the whole discussion then? Shouldnt something like if (mode == 0) multiplier = 1; else if (mode == 1) multiplier = Number( window .getComputedStyle(document.body) .getPropertyValue("font-size") .match(/\d+/)[0] ); else if (mode == 2) multiplier = window.innerHeight; else multiplier = 0; solve any problems? I mean it shouldnt matter in what unit they are returned, as long as you know the unit...

masayuki-nakano commented 4 years ago

@CWies because we got some web-compat issues which some web apps don't check deltaMode even though they refer delta(X|Y).

CWies commented 4 years ago

Well but that should be their issue, right?

masayuki-nakano commented 4 years ago

@CWies Right, but such web developers check only on Chrome typically (meaning which has most market share). Therefore, other browsers may need to use same behavior (i.e., using DELTA_MODE_PIXEL) for their users. However, if all browsers just do so, web developers who want raw delta value of native events cannot treat them anymore. Therefore, before switching Firefox's behavior, I suggest new API to retrieve raw delta value.

karlcow commented 3 years ago

See also Intent to ship: Return pixel deltas in wheel event if deltaMode is not checked by authors

( s/WheelEvent.delat/WheelEvent.delta/ in the title )

karlcow commented 3 years ago

A bit of status here.

Note that this debate was referenced in https://github.com/svgdotjs/svg.panzoom.js/issues/67 And this is the way they fixed it https://github.com/svgdotjs/svg.panzoom.js/pull/68/files

We are also having yet another discussion on zoomPan function for openstreetmap related to the same topics. https://github.com/webcompat/web-bugs/issues/73148

In Mozilla bug, deltaMode default should be DOM_DELTA_PIXEL, @emilio exposed DOM_DELTA_PIXEL rather than DOM_DELTA_LINES to the web

Important: https://bugzilla.mozilla.org/show_bug.cgi?id=1392460#c34

It returns pixels unless deltaMode is checked before getting the delta. If you prefer pixels, you can get the delta before checking deltaMode (and deltaMode would return pixels with the fix for this bug).

This is available only on Nightly so far.

zcorpan commented 1 year ago

Important: https://bugzilla.mozilla.org/show_bug.cgi?id=1392460#c34

It returns pixels unless deltaMode is checked before getting the delta. If you prefer pixels, you can get the delta before checking deltaMode (and deltaMode would return pixels with the fix for this bug).

This behavior is now shipping in Firefox but is causing confusion for web developers, see https://github.com/pixijs/pixijs/issues/8970

I think we should instead do what @RByers suggested in https://github.com/w3c/uievents/issues/181#issuecomment-394918199

@NavidZ and @smfr do you have thoughts about @masayuki-nakano computation cost concern in https://github.com/w3c/uievents/issues/181#issuecomment-537811017 ?

thasmo commented 1 year ago

This behavior is now shipping in Firefox but is causing confusion for web developers, see https://github.com/pixijs/pixijs/issues/8970

Wow, that oddity tripped me up bad.

trusktr commented 5 months ago

I'd like to propose that UI Events should declare that WheelEvent.delatMode always return WheelEvent.DOM_DELTA_PIXEL and WheelEvent.delta(X|Y|Z) are delta values in pixels

Even in cases when this is already true across browsers, deltaX/Y/Z vary wildly not only across browsers, but across operating systems, and maybe even across hardware.

Is there something we can add to specs to fix this so that we can get a value that is consistent across all browser and OS combinations?

Uses cases where this it is problematic is when building custom interaction using mouse and touchpad scroll, for example zoom controls for a 3D scene using canvas (webgl/webgpu) for custom rendering. It is nearly impossible to make the behavior of a custom interaction behave the same in all browser/OS combos, so much so that most people just pick a sensitivity value that is good enough across all browsers (a little slow in some, a little fast in some, but overall acceptable across all).

And even today after the Firefox bug with is already fixed, the deltaMode behavior still varies across browsers.

Examples of inconsistencies in the wild:

zaygraveyard commented 5 months ago

I would like to also add that on macOS the scroll direction can be flipped (natural vs normal) which renders scroll-based interactions that don't scroll content (as @trusktr mentioned) inconsistent unless the scroll event includes this information (if the direction is flipped). Not to mention macOS's inertial scrolling.

For context, I'm a developer of an online data visualizer for exploring satellite data on a map (the Ocean Virtual Lab is an example). The scroll interaction is used in multiple components:

And I have yet to find a way to iron-out the inconsistencies in scroll events between different browser and platforms 😔

PS: I apologize if these problems are out-of-scope for this issue, I just don't know where else to go

zaygraveyard commented 1 month ago

After conducting a survey on wheel deltas, I found the deprecated wheelDelta* properties are generally more consistent across platforms and browsers. Motivated by these findings we updated the web app mentioned above to use wheelDelta* properties, except on Apple systems (macOS, iOS, iPadOS, ...) for which we still use delta* properties.

Example:

function getWheelDelta(event) {
  if (isApple) {
    // Note that deltaMode MUST be accessed BEFORE delta* in order to get
    // non-pixel values in Firefox.
    // See https://github.com/w3c/uievents/issues/181

    switch (event.deltaMode) {
    default:
    case event.DOM_DELTA_PIXEL:
      return [event.deltaX / 120, -event.deltaY / 120];
    case event.DOM_DELTA_LINE:
    case event.DOM_DELTA_PAGE:
      // Discard the delta value, just take the sign
      return [Math.sign(event.deltaX), Math.sign(-event.deltaY)];
    }
  } else {
    return [-event.wheelDeltaX / 120, event.wheelDeltaY / 120];
  }
}
karlcow commented 1 month ago

@zaygraveyard what do you use for isApple?

zaygraveyard commented 1 month ago

@karlcow The detection is based on navigator.platform and is very basic, but works well enough:

const isApple = /^Mac|iPhone|iPod|iPad/i.test(navigator.platform);