web: Map pointer coords through getBoundingClientRect#23659
web: Map pointer coords through getBoundingClientRect#23659evil1morty wants to merge 2 commits intoruffle-rs:masterfrom
Conversation
`MouseEvent.offsetX/Y * devicePixelRatio` produces wrong stage coordinates whenever the canvas (or any ancestor) has a CSS transform applied — `transform: scale(0.5)` on a parent shrinks the visual rect but leaves `client_width` (the layout box used to size the canvas backing buffer) untouched, while `offsetX` reflects the visual position on Chromium. Click positions register at the wrong stage location. Browser zoom worked because zoom changes layout uniformly. Compute `(client_x - rect.left) * canvas.width / rect.width` (and the y equivalent) so the stage coord is always derived from the visual rect and the backing-buffer size, which together describe the actual pixel mapping regardless of CSS transforms, browser zoom, or DPR. Adds the `DomRect` web-sys feature for `Element::get_bounding_client_rect`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Can you add an integration test for it under |
|
Added an integration test under
Three cases — one per callback (pointerdown/up/move) — to satisfy the request that each one be exercised. |
fc986fd to
c3f08b8
Compare
|
The CI logs show the pointer-coord values were exactly right — Force-pushed an amend that updates each expected list to include the leading move:
That matches the trace order the test framework already saw in the failed run. |
The host page wraps a 200x200 SWF in a `transform: scale(0.5)` parent, so the canvas's visual rect is 100x100 while the backing buffer stays 200x200. Each pointer callback is exercised with a synthetic PointerEvent dispatched at known clientX/Y inside that visual rect, and the AS3 stage handlers trace the resulting (stageX, stageY). The expected stage coordinates assume the new ratio-based mapping in `pointer_to_stage_coords` — under the previous `offset_x * dpr` implementation each test would land off by a factor of 2. Covers pointermove, pointerdown, and pointerup — the three callbacks the fix touches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c3f08b8 to
00b9f29
Compare
|
How did you notice this issue? Are you scaling the Ruffle canvas on your website? |
|
I noticed it while testing on https://safariislandsgame.com/play/?lang=en $("#all").css("zoom", scale);
$("#all").css("-moz-transform", "scale(" + scale + "," + scale + ")");
$("#all").css("MozTransform", "scale(1)");
$("#all").css("MozTransformOrigin", "center");So on Firefox they use transform: scale(...), and on Chromium they use the zoom property |
Description
Pointer events arriving at the SWF stage are mis-mapped whenever the canvas (or any ancestor) is rendered at a different visual size than its layout box — most commonly under the CSS
zoomproperty, but also under CSS transforms. Concretely, withzoom: 0.5on a parent of the player, clicks register at the wrong stage location.The current code in
web/src/lib.rsis:Two things break:
canvas.client_width()timesdevice_pixel_ratio. Underzoom: 0.5the layout box is unchanged at e.g. 1000 CSS px and the backing buffer is 1000 px — but the canvas is visually 500 px on screen.MouseEvent.offsetXon Chromium reports the position relative to the visually rendered rect, not the untransformed padding edge. Multiplying that bydevice_pixel_ratiogives a value scaled to 0..(visual width × DPR), which doesn't match the 0..(layout width × DPR) the backing buffer expects.This PR replaces
offsetX/Y * device_pixel_ratiowith a small helper that computes coordinates fromclientX/YandgetBoundingClientRect(). The rect reflects whatever CSSzoom/ transforms / positioning the canvas ends up with, and the backing buffer iscanvas.width × canvas.height. The ratio(client - rect.origin) * canvas_size / rect_sizeis therefore correct under any combination of:zoomon the canvas or any ancestortransform: scale, etc.)Three call sites updated (
pointermove,pointerdown,pointerup); the wheel event doesn't pass a position so it's untouched.Element::get_bounding_client_rectreturns aDomRect, soDomRectis added to the web-sys feature list.Testing
Reproducing the original bug:
-/+zoom buttons — these set CSSzoomon a wrapper. Clicks register offset from where the user clicks.zoom: 0.7on a parent of<ruffle-player>. Same result.After this PR, clicks land at the cursor in both cases. Also verified with browser zoom at 75% / 100% / 125% / 150%, and
transform: scale(which already happened to map correctly via offsetX, but is now consistent with thezoompath through the same helper).cargo fmt --allclean.cargo clippy -p ruffle_web --target wasm32-unknown-unknownzero warnings.No new automated test added — couldn't find an existing harness for pointer-event mapping under DOM transforms; happy to add one if reviewers point me at a fixture pattern.
Checklist