javascriptangularangularjsmathcanvas

How to correctly calculate canvas coordinates when scale is applied?


I am working on a canvas application and I want to add a zoom feature. However, I am struggling to draw pixels after any zoom thats greater than 1 is applied.

It just draws the pixel on the place, where it would be correct if the origin was at the left top corner. However, this is not always the case of course, since the user can zoom in on any part they like.

I need a calculation that finds the correct pixel, while zoom and translate is applied to the context of my canvas.

Here is my zoom function

 zoom(e: WheelEvent) {
   this.zoomLevel -= e.deltaY * 0.01;
   this.zoomLevel = Math.round(Math.max(1, Math.min(this.zoomLevel, 10)));
   this.resizeCanvas();

   const rect = this.canvas.getBoundingClientRect();

   if (this.zoomLevel == 1) {
     this.originX = 0;
     this.originY = 0;
   }
   else {
     this.originX = Math.round((e.clientX - rect.left) / (rect.right - rect.left) * this.dimensions[0])
     this.originY = Math.round((e.clientY - rect.top) / (rect.bottom - rect.top) * this.dimensions[1])
   }
   this.ctx.translate(this.originX, this.originY);

   this.ctx.scale(this.zoomLevel, this.zoomLevel)

   this.ctx.translate(-this.originX, -this.originY);

   this.drawAllPixels();


 }

Here is the function where I am trying to calculate the pixels X and Y index, over which the user is hovering

onHover(e: any) {
  const mouseX = e.clientX;
  const mouseY = e.clientY;

  const rect = this.canvas.getBoundingClientRect();

  const scaleFactor = this.zoomLevel;
  console.log(this.originX, this.originY, scaleFactor)
  const pixelX = Math.floor((mouseX - rect.left) * (this.dimensions[0] / rect.width)); // This formular only works when this.zoomLevel is 1
  const pixelY = Math.floor((mouseY - rect.top) * (this.dimensions[1] / rect.height)); // This formular only works when this.zoomLevel is 1



  const p = this.pixelArr.find(item => item.x === pixelX && item.y === pixelY);

  this.drawPreviewPixel(pixelX,   pixelY, p?.color);

I am able to zoom in and out the canvas, but I am having trouble calculating the correct canvas coordinates from screen coordinates when a zoom and origin is applied.

The this.drawPreviewPixel successfully works on any zoom when I manually input the X and Y index (where the pixel should be drawn).

I also can successfully use drawPreviewPixel, when I zoom in at the exact left top corner.


Solution

  • The answer for me was the following formular:

    For x: Math.floor(mouseX / width * this.dimensions[0] / this.zoomLevel + (1 - 1 / this.zoomLevel) * this.originX);

    For y: Math.floor(mouseY / height * this.dimensions[1] / this.zoomLevel + (1 - 1 / this.zoomLevel) * this.originY);