Search code examples
javascripthtmlhtml5-canvas

HTML canvas setTranslate does not work with onClick


I have an HTML canvas with some content. This content needs to be draw translated to the canvas. That is working. But now I need to detect which object is clicked, so I need to search in the coordinates of the objects to find an matching one. But since the objects are drawn at a different x, y position then known I don't know how to match them.

I have created this fiddle with the situation. I want to highlight the correct object when clicked. One thing to keep in mind is that this canvas is used in an environment (HMI screen connected with PLC) where I don't have the full functions which are normally available in a browser. That means also that I only get an x and y coordinate on the 'onClick' function.

https://jsfiddle.net/gkho89wd/

This code is not the cleanest but I use this to try some things. Quick explanation of this code:

I have an array with some objects (boxes)

const rawPositions = [];

every position has some properties including x and y, width and length. Those are used to draw some rects on the canvas. I also calculate an 'ratioX' and 'ratioY' to scale everything. Those x and y are used to actually draw.

Before I draw the rects, I'm doing an 'setTransform' to horizontally flip the canvas:

ctx.setTransform(1, 0, 0, 1, 0, ctx.height);

How can I translate the coordinates from the click event to the 'original' ones?


Solution

  • You can update the findShape function to take into account the transformation applied to the canvas. You should flip the y coordinate back and then use that to search for the clicked object.

    async function findShape(oX, oY) {
      console.log('findShape', oX, oY)
    
      // Flip the y coordinate back to match the transformed canvas
      const flippedY = ctx.height - oY;
    
      const clickedBox = placedCarton.findIndex(b => {
        return (b.ratioX <= oX) && (b.ratioX + b.width >= oX) &&
               (b.ratioY <= flippedY) && (b.ratioY + b.length >= flippedY);
      });
    
      moveArrows = [];
      positions = [];
    
      if (clickedBox >= 0) {
        placedCarton.forEach(b => b.active = false);
        placedCarton[clickedBox].active = true;
        activeBox = clickedBox;
    
        updateCartonCanvas();
        const box = placedCarton[clickedBox];
    
        await window.savePositions();
    
        const prePos = window.prePos;
        const postPos = window.postPos;
    
      } else {
        console.log('no box found');
      }
    }
    

    This modification takes into account the fact that the canvas is flipped vertically. By flipping the y coordinate back (using ctx.height - oY), you can now correctly detect which object is clicked.