skip to Main Content

The following code presents the issue, when you click in the gray area a + sign is added to #node-container. Now if you zoom out using your mouse wheel or touch pad and then pan along x or y axis with click and drag and then try to click to place the + the placement is very wrong.

I want to place the + sign exactly at the mouse position

Codepen

let canvas = document.getElementById('canvas');
let nodeContainer = document.getElementById('node-container');
let pz = {
    offset: {
      x: 0.0,
      y: 0.0
    },
    zoom: 1
  };
let isDragging = false;
let c = 0;

// pan and zoom

const updateDom = () => {
  let x = pz.offset.x * pz.zoom +'px';
  let y = pz.offset.y * pz.zoom +'px';
  let z = pz.zoom;
  nodeContainer.style.transform = `translate(${x}, ${y}) scale(${z})`
}

const handleMouseDown = () => {
  isDragging = true;
};

const handleMouseUp = () => {
  isDragging = false;
};

const handleMouseMove = (e) => {
    if (!isDragging) {
      return;
    }

    if (e.buttons !== 1) {
      isDragging = false;

      return;
    }
  
    pz.offset = {
        x: pz.offset.x + e.movementX / pz.zoom,
        y: pz.offset.y + e.movementY / pz.zoom
      };
  
    updateDom();
  
  };

const handleWheel = (e) => {
      e.preventDefault();
      e.stopPropagation();

      if (e.ctrlKey) {
        const speedFactor =
          (e.deltaMode === 1 ? 0.05 : e.deltaMode ? 1 : 0.002) * 10;
        const pinchDelta = -e.deltaY * speedFactor;
        pz.zoom = Math.min(
              1.3,
              Math.max(0.1, pz.zoom * Math.pow(2, pinchDelta))
            )
      }
  
  updateDom();
}

canvas.onwheel = handleWheel;
canvas.onmousedown = handleMouseDown;
canvas.onmouseup = handleMouseUp;
canvas.onmousemove = handleMouseMove;


canvas.addEventListener('click', (e) => {
  let nc = nodeContainer.getBoundingClientRect();
  let pzx = pz.offset.x / pz.zoom;
  let pzy = pz.offset.y / pz.zoom;
  
  let marker = document.createElement('span');
  marker.innerText = '+'
  marker.style.fontSize = '19pt';
  marker.style.position = 'absolute';
  
  let nx = (e.x/pz.zoom) + nc.x;
  let ny = (e.y/pz.zoom) + nc.y;
  
  marker.style.left = nx - pzx + 'px'
  marker.style.top = ny - pzy + 'px'
  
  nodeContainer.appendChild(marker);
  
});
  <script src="https://cdn.tailwindcss.com"></script>
<div class="grid grid-cols-2 divide-x">
  <div id="canvas" class="relative h-screen bg-gray-100">
    <div id="container" class="absolute origin-top-left w-0 h-0 overflow-visible">
      <div id="node-container" class="absolute w-0 h-0 overflow-visible"></div> <!-- end of #node-container -->
    </div> <!-- end of #container -->
  </div> <!-- end of canvas & left col -->
  <div>
  </div> <!-- end of right col -->
</div>

2

Answers


  1. In your code you added the nodeContainer’s x and y to your + , you can try removing it. like:

    From:

      let nx = (e.x/pz.zoom) + nc.x;
      let ny = (e.y/pz.zoom) + nc.y;
    

    to:

      let nx = (e.x/pz.zoom);
      let ny = (e.y/pz.zoom);
    
    Login or Signup to reply.
  2. <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Canvas Zoom and Pan</title>
       <script src="https://cdn.tailwindcss.com"></script>
        <style>
        #canvas {
          position: relative;
          overflow: hidden;
        }
        #node-container {
         position: absolute;
          top: 0;
          left: 0;
        }
        .marker {
          position: absolute;
          font-size: 19pt;
        }
      </style>
    </head>
    <body>
      <div class="grid grid-cols-2 divide-x">
       <div id="canvas" class="relative h-screen bg-gray-100">
          <div id="container" class="absolute origin-top-left w-0 h-0 overflow- 
     visible">
          <div id="node-container" class="absolute w-0 h-0 overflow-visible"> 
     </div>
          </div>
        </div>
        <div></div>
      </div>
    
     <script>
        let canvas = document.getElementById('canvas');
        let nodeContainer = document.getElementById('node-container');
        let pz = {
          offset: { x: 0, y: 0 },
          zoom: 1
        };
        let isDragging = false;
    
    
        const updateDom = () => {
          let x = pz.offset.x * pz.zoom + 'px';
          let y = pz.offset.y * pz.zoom + 'px';
          let z = pz.zoom;
          nodeContainer.style.transform = `translate(${x}, ${y}) scale(${z})`;
        };
    
        const handleMouseDown = () => {
          isDragging = true;
        };
    
    const handleMouseUp = () => {
      isDragging = false;
    };
    
    const handleMouseMove = (e) => {
      if (!isDragging) return;
    
      pz.offset = {
        x: pz.offset.x + e.movementX / pz.zoom,
        y: pz.offset.y + e.movementY / pz.zoom
      };
    
      updateDom();
    };
    
    const handleWheel = (e) => {
      e.preventDefault();
    
      if (e.ctrlKey) {
        const speedFactor = (e.deltaMode === 1 ? 0.05 : e.deltaMode ? 1 : 0.002) * 10;
        const pinchDelta = -e.deltaY * speedFactor;
        const zoomFactor = Math.pow(2, pinchDelta);
        pz.zoom = Math.min(1.3, Math.max(0.1, pz.zoom * zoomFactor));
    
        const rect = canvas.getBoundingClientRect();
        const mouseX = e.clientX - rect.left;
        const mouseY = e.clientY - rect.top;
    
        pz.offset.x -= mouseX / pz.zoom * (zoomFactor - 1);
        pz.offset.y -= mouseY / pz.zoom * (zoomFactor - 1);
      }
    
      updateDom();
    };
    
        canvas.onwheel = handleWheel;
        canvas.onmousedown = handleMouseDown;
        canvas.onmouseup = handleMouseUp;
        canvas.onmousemove = handleMouseMove;
    
    
        canvas.addEventListener('click', (e) => {
          const rect = canvas.getBoundingClientRect();
          const mouseX = (e.clientX - rect.left) / pz.zoom - pz.offset.x;
          const mouseY = (e.clientY - rect.top) / pz.zoom - pz.offset.y;
    
          let marker = document.createElement('span');
          marker.innerText = '+';
           marker.className = 'marker';
          marker.style.left = mouseX + 'px';
         marker.style.top = mouseY + 'px';
    
          nodeContainer.appendChild(marker);
        });
      </script>
    </body>
     </html>
    

    Try this and see if it works

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search