Home > OS >  drawing circle starts at wrong position
drawing circle starts at wrong position

Time:11-24

I wrote a function which allows me to draw circles on a canvas. Everything works fine, but the start position of my canvas is not the same position as my mouse position.

const stopDrawingCircle = () => {
    setIsDrawing(false);
    console.log(currentImage);
}
//Setting start position
const startDrawingCircle = (event:any) => {
    currentImage.circle.x = event.clientX;
    currentImage.circle.y = event.clientY;
    currentImage.circle.radius = 0;
    setIsDrawing(true);
};
const drawCircle = function(event:any){
    if(!isDrawing)
      return;
    let canvas = canvasRef.current as HTMLCanvasElement | null; 
    let ctx = canvas?.getContext('2d')
    //seems to be the problem console log at start says the result of this is zero
    var currentX = currentImage.circle.x - event.clientX;
    var currentY = currentImage.circle.y - event.clientY;
    currentImage.circle.radius = Math.sqrt(currentX * currentX   currentY * currentY)
    if(canvas != null && ctx != null){
        ctx.beginPath();
        ctx.arc(currentImage.circle.x, currentImage.circle.y, currentImage.circle.radius, 0, Math.PI*2);
        ctx.fill();
    }
}
return(
  <div className="main">
    <div id="imageSection">
      <canvas id="canvas" 
        onm ouseDown={startDrawingCircle}
        onm ouseUp={stopDrawingCircle}
        onm ouseMove={drawCircle}
        ref={canvasRef}> </canvas>       
    </div>
  </div>
)

The result of drawing a circle is like this:

example of fail

I started with my mouse in the center of the image and he draws it in the "lower right corner"

CodePudding user response:

event.clientX and event.clientY are relative to the current screen, not element being clicked.

MDN MouseEvent.clientX docs:

clientX
A number, defaulting to 0, that is the horizontal position of the mouse event on the client window of user's screen;

Use event.offsetX and event.offsetY to get the mouse position relative to the event target.

However, since you're using React, you need to use event.nativeEvent.offsetX and event.nativeEvent.offsetY because react doesn't copy the offset properties onto their event proxy object.


Full code example:

import {useState, useRef} from "react";

const currentImage = {
  circle: {
    x: 0,
    y: 0,
    radius: 10,
  }
};

export default function App() {
  const [isDrawing, setIsDrawing] = useState(false);
  const canvasRef = useRef<HTMLCanvasElement>(null);

  const stopDrawingCircle = () => {
      setIsDrawing(false);
      // console.log(currentImage);
  }
  //Setting start position
  const startDrawingCircle = (event:any) => {
      currentImage.circle.x = event.nativeEvent.offsetX;
      currentImage.circle.y = event.nativeEvent.offsetY;
      currentImage.circle.radius = 0;
      setIsDrawing(true);
  };
  const drawCircle = function(event:any){
      if(!isDrawing)
        return;
      let canvas = canvasRef.current as HTMLCanvasElement | null; 
      let ctx = canvas?.getContext('2d')
      //seems to be the problem console log at start says the result of this is zero
      var currentX = currentImage.circle.x - event.nativeEvent.offsetX;
      var currentY = currentImage.circle.y - event.nativeEvent.offsetY;

      currentImage.circle.radius = Math.sqrt(currentX * currentX   currentY * currentY)
      if(canvas != null && ctx != null){
          ctx.beginPath();
          ctx.arc(currentImage.circle.x, currentImage.circle.y, currentImage.circle.radius, 0, Math.PI*2);
          ctx.fill();
      }
  }
  return(
    <div className="main">
      <div id="imageSection">
        <canvas id="canvas" 
          onm ouseDown={startDrawingCircle}
          onm ouseUp={stopDrawingCircle}
          onm ouseMove={drawCircle}
          ref={canvasRef}> </canvas>       
      </div>
    </div>
  )
}

CodePudding user response:

It's hard to run your example because it does not seem to be a complete one.

I suspect what your error is and I think this simple example will be enough for you to fix your issue.

const canvas = document.getElementById('canvas');

const drawCircle = function(event){
  const ctx = canvas.getContext('2d');
  const canvasClientRect = canvas.getBoundingClientRect();
  
  const { clientX, clientY } = event; // absolute click pos
  
  const x = clientX - canvasClientRect.left; // calculate relative x
  const y = clientY - canvasClientRect.top; // calculate relative y
  
  ctx.beginPath();
  ctx.arc(x, y, 20, 0, Math.PI * 2);
  ctx.fill();
}

canvas.addEventListener('click', drawCircle);
  <div className="main">
    <h1>
    Example
    </h1>
    <div id="imageSection">
      <canvas id="canvas" width="500px" height="500px" style="border: 1px solid red;"></canvas>       
    </div>
  </div>

So as you can see getting the clicked X and Y coordinates is not enough and it's not relative to the canvas element and to draw on the canvas you need its relative coordinates.

If your canvas started at the very top left corner of your page it would not matter but if it's not there then it's important to get the canvas coordinates so you can calculate the click position properly.

So let's assume your canvas starts 50px from the top and 100px from the left. To each click on the canvas you need to subtract 50px for the X coordinate and 100px to the Y coordinate. With these relative values you can draw on canvas and the circle will be in the middle of your cursor every time.

  • Related