Fix pointer position when canvas has CSS transforms (e.g. rotate)#7278
Fix pointer position when canvas has CSS transforms (e.g. rotate)#7278roy7 wants to merge 1 commit intophaserjs:masterfrom
Conversation
The existing transformX/transformY methods convert pointer coordinates independently, which breaks when the canvas or a parent element has a CSS rotation — the X and Y axes become coupled. This adds transformXY which uses the DOMMatrix API to apply the inverse of accumulated CSS transforms, correctly mapping screen-space pointer positions back to canvas-local coordinates. Zero overhead when no CSS transforms exist. Closes phaserjs#7175 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
I was thinking about a PR for a different issue I was having locally, but it seems it might be working as intended so decided to just use my local workaround and not report it as a bug. (Phaser's useHandCursor changes the cursor for all interactive objects under the pointer regardless of mask visibility or depth ordering, so masked/hidden objects still show a hand cursor. But the docs are clear masking is purely for graphics and not used for interactions. So I worked around it by manually checking for cursor changes instead of relying on useHandCursor.) However when exploring known issues I saw this one and figured I'd let Claude Code take a crack at it. I did test locally that this fixes the bug in the example HTML provided in the original issue, and it passed the test suite, so I'm sending it in for your review in case you think it is worthwhile. Thanks for reviewing! |
Summary
transform(e.g.rotate(90deg)for forcing landscape in portrait-locked webviews)transformXYmethod to ScaleManager that accounts for CSS rotation/scale/skew viaDOMMatrixInputManager.transformPointerto use the combined XY transform instead of separatetransformX/transformYcallsDetail
Closes #7175.
The existing
transformXandtransformYmethods convert pointer coordinates independently:This breaks when the canvas has a CSS rotation because the X and Y axes become coupled — a screen-space X offset maps to both game X and Y. The simple subtract-and-scale approach can't account for this.
The fix adds
getInverseCSSTransform()which walks the DOM tree from the canvas to the document root, composing any CSStransformvalues found along the way. It extracts the rotation/scale/skew portion (translation is already handled bygetBoundingClientRect) and caches its inverse. The newtransformXYmethod uses this inverse to convert screen-space pointer offsets back to the canvas's local coordinate system before scaling to game coordinates.The inverse matrix is only recomputed in
updateBounds()(on resize/orientation change), not on every pointer event.Test plan
transform: rotate(90deg)on canvas parent (reproduction from Pointer position incorrect of canvas is rotated #7175)🤖 Generated with Claude Code
Note
Medium Risk
Touches core pointer coordinate transformation logic in
ScaleManager/InputManager, which could impact all mouse/touch input; mitigation is the cached inverse matrix and explicit fallback to the prior math when no CSS transforms are present.Overview
Fixes incorrect pointer coordinates when the canvas (or an ancestor) has CSS transforms like rotation/skew by introducing a coupled
ScaleManager.transformXYpath.ScaleManagernow caches an inverse accumulated CSS transform matrix (viaDOMMatrix) duringupdateBounds()and uses it to convert(pageX,pageY)into game-space coordinates;InputManager.transformPointerswitches from separatetransformX/transformYcalls to this new combined transform, falling back to the existing offset+scale behavior when no CSS transforms are detected.Reviewed by Cursor Bugbot for commit 0625d14. Bugbot is set up for automated code reviews on this repo. Configure here.