Moseleyi
Moseleyi

Reputation: 2879

Placing an element within scaled + transformed div based on outside cursor position

After four hours of head-scratching, I think I need some help. Will try to explain it as well as possible.

I'm trying to place a pin on a map that can be zoomed in and moved around.

The scaling happens with the use of PanZoom script that applies transform:scale translate to a div: https://github.com/timmywil/panzoom

Here's my HTML:

<div id="map">
  <div id="map-in">
    <img src="/assets/map.jpg" />
    <div id="pin-a">
      <img src="/assets/pin.jpg" />
    </div>
  </div>
  <div id="map-context-menu" class="map-context-menu">
      <div id="map-point-a" class="mcm">Ustaw punkt A</div> 
  </div>
</div>

The pin-a element is hidden by default and given a class show via JavaScript.

Here's the image: enter image description here

The process is that I right click to open a new contextual menu and then upon the click "Ustaw punkt A" I drop a pin. As you can see the context menu location is calculated correctly and opens when my mouse cursor is. However when I want to use that position to place a pin, considering the image is scaled and moved, my math is going completely out of whack.

The pin is within the div that gets CSS transformations in order for it to be sticky.

Here's the JavaScript I use

let elem = document.getElementById('map-in')
panzoom = Panzoom(elem, {
  maxScale: 5,
  contain: "outside"
});

$("#map-in").on("contextmenu", function(e) {
  e.preventDefault();

  var elm = $("#map");
  point_left = e.pageX - elm.offset().left;
  point_top = e.pageY - elm.offset().top;

  $("#map-context-menu").css({
      top: point_top,
      left: point_left
  }).addClass("show"); 
});

$("#map-point-a").on("click", function(e) {
  e.preventDefault();
  
  /* getPan returns an object {x: number, y: number} taken from styles */
  let pan_left = panzoom.getPan().x;
  let pan_top  = panzoom.getPan().y;
 
  /* returns value of scale() */
  let pan_scale = panzoom.getScale();

  /* Point left and point top are variables saved when context menu is opened */
  let point_x = point_left;
  let point_y = point_top; 

  $("#pin-a").css({
      top: point_y,
      left: point_x
  }).addClass("show");
});

When I use wheel scrolling here's an example styles applied to the inner container:

cursor: move; user-select: none; touch-action: none; transform-origin: 50% 50%;
transition: none 0s ease 0s; transform: scale(1.49182) translate(-9.81022px, 2.35833px);

What I don't undertand is how to apply the math. Let's say my context menu opens at 300,300 relative to the main outsider container.

I have tried numerous formulas:

  1. (point_left - pan_left) * pan_scale
  2. (point_left + (pan_left * -1)) / pan_scale
  3. point_left - (pan_left / pan_scale)

But none of them works. I'm assuming I'm missing a crucial part of how the transformation works and hence can't figure out the math but I have no other ideas.. Essentially I'm trying to find out how to translate X,Y of the cursor relative to the main container to the X,Y of the transformed image.

Concrete example

Position of the cursor when opening context menu: x: 458, y: 519

Scale: 1.16

Pan: x: 97, y: -97

The yellow dot is where I actually right clicked: enter image description here

Upvotes: 2

Views: 86

Answers (1)

johnidel127
johnidel127

Reputation: 189

This took me way longer than I should have found:

You need to set transform-origin: 0 0; on the container element for the map.

Even if you don't have a transform value set, for some reason, setting this explicitly fixed the problem.

Upvotes: 0

Related Questions