Skip to content

ScreenSpaceEventHandler: CSS transform on canvas parent causes mouse interaction offset #13493

@darcyvdd

Description

@darcyvdd

What happened?

When a parent DOM element wrapping the CesiumJS canvas has a CSS transform: scale(n) applied (where n != 1), all mouse interaction events (clicks, hover, camera controls) are offset. The registered click position does not match the visual position on the globe.

The root cause is in ScreenSpaceEventHandler.getPosition(). It uses getBoundingClientRect() to get the element's position, but doesn't account for the scale ratio between the bounding rect dimensions and element.clientWidth/Height. When CSS transforms scale the container, getBoundingClientRect() returns the scaled dimensions while clientWidth/Height returns the unscaled dimensions, creating a mismatch.

Expected behavior: Mouse events should register at the correct visual position regardless of CSS transforms applied to ancestor elements.

This issue was surfaced on the forum here: https://community.cesium.com/t/is-there-a-standard-solution-for-interaction-issues-caused-by-transform-or-is-it-an-unexpected-bug/46065. The forum user also proposes a fix they found on the forum. I will repeat here as well:

Approach: Directly modify the source code in ScreenSpaceEventHandler.js, adding scale calculation to the getPosition method:

const rect = element.getBoundingClientRect();
const scaleX = rect.width / element.clientWidth;
const scaleY = rect.height / element.clientHeight;

result.x = (event.clientX - rect.left) / scaleX;
result.y = (event.clientY - rect.top) / scaleY;

Result: Camera controls and interaction events both produce the correct results.

Reproduction steps

  1. Wrap the Cesium container div in a parent element with transform: scale(0.5) (or any value != 1)
  2. Add a billboard or entity to the scene
  3. Click on the billboard. The click registers at an offset position, not where the billboard visually appears

Sandcastle example

https://sandcastle.cesium.com/#c=ZZLdauMwEIVfZfCy4CxZ2SUb0jpuWMheFvaisL0RlLE1cURlyUhyQlry7iv/1S29sTVnznyckS3rxlgPPwAd7MnJtoaDNTXwqOwrHm25loPpEbUo0XlFk2dWeh/XpdHOw0nSmSzcg6bzSGX/ei2euHujPUpNlkeLfnKYYaS99JIcQyHiN64BGuOCYnQ2kfZofTihXrEuxh+qLJGLf27WbH232WyW8Ctl6er2drVYdoBCKlUYtCKDHggga6woC/kZS/qzSwb284OpzLM5kVV4YY2ueNQjruF5XWyjZZQ7f1G068Tf47W0VsUB5KluFIZkSdGWL+RZ6dywGsA3b1G7g7H1k8VmiiGkCxOXDAplypftIM7rYuGMaj2NDW+aDNKxUHTwc3WWwh8zuEnT76NyJFkd/SfpPUIGrkRFccrWi7535TpPpr1yIU8gxT2PPmXmUb/z3P3yGaFU6FzoHFqlHuVr+CV2eRL8HXR8f8Qrg0Lq6u9w2Z35eLN7GETGWJ6E8n1+DmWMKtB+YP8H

Environment

Browser: Chrome (148.0.7778.97 (Official Build) (64-bit))
CesiumJS Version: 1.141.0
Operating System: Windows 11

AI acknowledgment

  • I used AI to generate this issue report.
  • (If the above is checked) I have reviewed the AI-generated content before submitting.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions