w3c/pointerlock

movementX/Y coordinate space

EiraGe opened this issue · 10 comments

The spec define movementX/Y from screenX/Y. However, while all major browser (except Edge) having screenX/Y in DIP(w3c/uievents#150), movementX/Y implementation are kind of mess here:

movementX/Y Edge Chrome Firefox Safari
coordinate space Physical pixel Physical pixel CSS pixel DIP(buggy?)
Scaled by device-scale-factor(OS High DPI setting) X X
Scaled by page zoom X X X
Scale by Pinch-zoom X X X X

It would be nicer and clearer to have better interoperability.
WDYT? @NavidZ @mustaqahmed @smaug----

I agree with Ella re the incompatible implementations.

The current movementX/Y definition in the spec (difference between consecutive screenX/Y values) is problematic from another perspective: screenX/Y spec itself doesn't match the reality! The spec defines screenX/Y as CSS pixels, but most major browsers have DIPs. As a result, browsers have two interpretations of the PointerLock spec: implement movementX/Y either

  • as DIPs to match most browsers' screenX/Y, or
  • as CSS pixels to match screenX/Y spec.

Chrome's movementX/Y-in-physical-pixels clearly doesn't look great. We want to change it, see the following possible ways forward:

  1. Modify movementX/Y to DIP to match screenX/Y reality, and keep the PointerLock spec as is.
    This has two advantages: (i) seems like a safe change because sites shouldn't be affected by scale change in movementX/Y, and (ii) if we ever change screenX/Y spec to DIP in future, everything will be good from spec perspective.

  2. Modify movementX/Y to CSS pixels to match screenX/Y spec, and keep the PointerLock spec as is.
    This essentially pulls the screenX/Y problem (that spec doesn't not matching reality, see linked issue above) into PointerLock without a clear benefit: changing most browsers' screenX/Y to match spec is almost impossible; hopefully one day we will change screenX/Y to match browsers instead, which would then call for a change of movementX/Y to DIPs.

  3. Change PointerLock spec to make movementX/Y in physical pixels.
    This completely isolates movementX/Y from screenX/Y coordinate space (and corresponding spec-not-reflecting-reality problem).

Wondering what other browser vendors think about these three options (and possibly other we didn't see).

Modify movementX/Y to DIP to match screenX/Y reality, and keep the PointerLock spec as is.
This has two advantages: (i) seems like a safe change because sites shouldn't be affected by scale change in movementX/Y, and (ii) if we ever change screenX/Y spec to DIP in future, everything will be good from spec perspective.

So with this first way if we change screenX/Y spec to DIP first we don't need to change Pointerlock spec either. Right? I know that task has hit some road block. So I'm fine with changing the movementx/y to be DIP on its own. Not sure how to specify it very well though. Do we have all the values needed for that specified already?

So I'm fine with changing the movementx/y to be DIP on its own. Not sure how to specify it very well though. Do we have all the values needed for that specified already?

In my first and second points above, I am not proposing any normative spec change at all. I am proposing that browsers would pick one of the two possible interpretations of the current specs (I mean the two interpretations with bullets in my last post). There can be a non-normative note in PointerLock spec though.

Here is one more suggestion to deal with the situation from an API user perspective.

I'm implementing a slider/knob, that can be adjusted by dragging. For the adjustment during dragging, the distance traveled in clientX/Y coordinates can be used.
But when pointer lock is enganged during dragging, the movementX/Y coordinates have to be used instead. The obvious expectation here would be for movementX/Y to match the clientX/Y coordinate space. This seems to work on FF-77 but not Chrome-83 with devicePixelRatio=2. So the idea is:

  1. Modify movementX/Y to CSS to match clientX/Y (or pageX/Y) coordinate space, this needs a wording correction of the PointerLock spec.
    (i) Chrome would have to be fixed to match Firefox' calculations.
    (ii) Changing the screenX/Y spec has no effects on movementX/Y.

Alternatively, movementX/Y could be documented/speced to always be scaled up by devicePixelRatio and FF would need fixing. But that doesn't seem to be consistent with any other coordinate pair exposed by the mouse/pointer events. Related crbug#1092358.

Seriously, how long is this going to take? Are there any work arounds for now?

Can we please get this fixed. movementX, movementY, and movementZ are not reliable at all without consistent units.

For consistency, CSS pixel seem would be the best (a user can multiply by devicePixelRatio to convert to physical if they wish).

This will align with px in CSS which means that mapping movementX, for example, to a difference in width or transform will simply work. f.e.

el.style.width = currentWidth + event.movementX + 'px'

Right now, that will produce weird results where the element being moved or resized will move or resize differently than expected.

The workaround right now is to instead never use movement* properties, and calculate the value directly using some math using the client* properties.

const movementX = event.clientX - previousClientX
const movementY = event.clientY - previousClientY

This has been working better for me, but I haven't tested with user zoom.

It seems like basing it on CSS pixels is the way to make it result in correct calculation based on the web content. I've never wanted the delta to be anything other than aligned with the content (which means CSS pixels).

Here's a demo showing movementX is not usable in Chrome (on a macbook air m2 with dpr 2):

https://codepen.io/trusktr/pen/OJqROvd/823fc7ec003e4652d97ff13e32d91b80

When you drag left/right on the demo, notice the mouse moves different speed than then orange border.

Here's a demo using movementX = event.clientX - previousClientX, and it works as expected, dragging moves the box at the same speed as the mouse:

https://codepen.io/trusktr/pen/gOEwXdZ/6257b761fdc13f2b7c354ed2cf422af4

Here's a demo showing movementX is not usable in Chrome (on a macbook air m2 with dpr 2):

https://codepen.io/trusktr/pen/OJqROvd/823fc7ec003e4652d97ff13e32d91b80

When you drag left/right on the demo, notice the mouse moves different speed than then orange border.

Here's a demo using movementX = event.clientX - previousClientX, and it works as expected, dragging moves the box at the same speed as the mouse:

https://codepen.io/trusktr/pen/gOEwXdZ/6257b761fdc13f2b7c354ed2cf422af4

I would like to point out this is zoom level dependent. On my monitor the first demo worked fine until I zoomed in.

This is important topic

Can we bump the priority of this issue