Mouse cursor doesn't match with canvas

I have question: when I'm drawing a line in canvas, it seems the mouse position doesn't match with the canvas position, so whenever I draw, there is some distance between my cursor and the drawing line .. please help me with this problem, here is my code :

$(document).ready(function(){

        context = document.getElementById('canvasInAPerfectWorld').getContext("2d");


        $('#canvasInAPerfectWorld').mousedown(function(e){
          var mouseX = e.pageX - this.offsetLeft;
          var mouseY = e.pageY - this.offsetTop;

          paint = true;
          addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
          redraw();
        });

        $('#canvasInAPerfectWorld').mousemove(function(e){ 
          if(paint){
            addClick(e.pageX - this.offsetLeft, e.pageY - this.offsetTop, true);
            redraw();
          }
        });


        $('#canvasInAPerfectWorld').mouseup(function(e){
          paint = false;
        });

        $('#canvasInAPerfectWorld').mouseleave(function(e){
          paint = false;
        });         

});


    var clickX = new Array();
    var clickY = new Array();
    var clickDrag = new Array();
    var paint;

    function addClick(x, y, dragging)
    {
      clickX.push(x);
      clickY.push(y);
      clickDrag.push(dragging);
    }   

    function clear_canvas(){
        //alert('masuk claear');
        context.clearRect(0,0,context.canvas.width,context.canvas.height);  

    }

    function redraw(){        

      context.strokeStyle = "#df4b26";
      context.lineJoin = "round";
      context.lineWidth = 5;

      for(var i=0; i < clickX.length; i++) {        
        context.beginPath();
        if(clickDrag[i] && i){
          context.moveTo(clickX[i-1], clickY[i-1]);
         }else{
           context.moveTo(clickX[i]-1, clickY[i]);
         }
         context.lineTo(clickX[i], clickY[i]);
         context.closePath();
         context.stroke();
      }
    } 

Answers:

Answer

Inside your mouse event handlers, this refers to the window object and your this.offsetLeft is undefined.

You can use getBoundingClientRect to get the bounds of your canvas element:

// get a reference to your canvas element at the start of your app
var canvas=document.getElementById('canvasInAPerfectWorld');

// example mousedown handler

// get the current canvas offsets using getBoundingClientRect
var BB=canvas.getBoundingClientRect();
var offsetX=BB.left;
var offsetY=BB.top;        

// calculate the current mouse position relative to the canvas
// using e.client and the offsets calculated above
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);

If you canvas does not reposition relative to the viewport, you can get the offsets once at the start of your app so they don't need to be recalculated every time inside the mouse handler.

Answer

You could follow the solution in markE's answer (also found here).

Or you could do the following if your layout allows

  • Set canvas element to position relative
  • Use layerX and layerY to read the mouse position

This approach gives a little simpler code.

Both methods will be affected by padding and border thickness (they need to be subtracted if any is used). If you want border/padding it's better to wrap the canvas in a div and then style the div instead.

Example using relative positioned canvas

var c = document.querySelector("canvas"),
    ctx = c.getContext("2d");

ctx.font = "bold 16px sans-serif";

c.onmousemove = function(e) {
  
  var x = e.layerX,
      y = e.layerY;

  ctx.clearRect(0, 0, 300, 20);
  ctx.fillText("x: " + x + ", y: " + y, 10, 16);
};
div {padding:20px}
canvas {background:#eee; position:relative}
<div><div><canvas></canvas></div></div>

Example using getBoundingClientRect()

var c = document.querySelector("canvas"),
    ctx = c.getContext("2d");

ctx.font = "bold 16px sans-serif";

c.onmousemove = function(e) {
  
  var rect = this.getBoundingClientRect(),
      x = e.clientX - rect.left,
      y = e.clientY - rect.top;

  ctx.clearRect(0, 0, 300, 20);
  ctx.fillText("x: " + x + ", y: " + y, 10, 16);
};
div {padding:20px}
canvas {background:#eee; position:relative}
<div><div><canvas></canvas></div></div>

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us Javascript

©2020 All rights reserved.