mapbox / mapbox/mapbox-gl-js

CSS transform issue

Open
#7,701 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug :lady_beetle: needs discussion :speech_balloon:
Dominant language
TypeScript
Stars
12.4k
Forks
2.4k
PR merge metrics
No merged PRs in 30d

Description

When emit click, zoom, touch event on map, we calculate the mouse position in map as follows:

https://github.com/mapbox/mapbox-gl-js/blob/master/src/util/dom.js#L104-L111

```
DOM.mousePos = function (el: HTMLElement, e: any) {
const rect = el.getBoundingClientRect();
e = e.touches ? e.touches[0] : e;
return new Point(
e.clientX - rect.left - el.clientLeft,
e.clientY - rect.top - el.clientTop
);
};
```

This process is simple like this:

![image](https://user-images.githubusercontent.com/1522494/49931810-a6e76480-ff02-11e8-9006-045994735416.png)

The calculation is correct on most cases, except the case that we add css transform property on .mapboxgl-canvas-container. For example, we add `rotate(10deg)`

![image](https://user-images.githubusercontent.com/1522494/49931985-fc237600-ff02-11e8-8fd2-8b8c544731a3.png)

If we scale or skew or even transform3d the canvas container, we also can not get the right position from `mousePos()`. The zoom, pan, touch, click behaviour will be affected:

https://jsfiddle.net/jingsam/3wh5grko/25/

Try to click somewhere on the map.

This [issue](https://github.com/mapbox/mapbox-gl-js/issues/6079) is also related to the same problem.

# Solution

What we want from `mousePos` is the position is **map space**, not the viewport space or other space. Luckily, we have [offsetX](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX) [offsetY](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX). [CSSOM](https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx) says:

> The offsetX attribute must follow these steps:
> 1. If the event’s dispatch flag is set, return the x-coordinate of the position where the event occurred **relative to the origin of the padding edge of the target node**, ignoring the transforms that apply to the element and its ancestors, and terminate these steps.
> 2. Return the value of the event’s pageX attribute.

offsetX and offsetY are just we want. so `mousePos` should changed as follows:

```
DOM.mousePos = function (el: HTMLElement, e: any) {
e = e.touches ? e.touches[0] : e;
return new Point(
e.offsetX,
e.offsetY
);
};
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/util/dom.js at DOM.mousePos and reproduce the transformed-container behavior using the linked JSFiddle. Check how click, zoom, touch, and pan events obtain map coordinates when CSS transforms are applied. Done means those interactions receive correct map-space positions under rotate, scale, skew, and transform3d cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, typescript
Domain
frontend, web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.