user3478148
user3478148

Reputation: 443

Canvas drawing tool issue

I'm practicing JavaScript by developing a drawing tool, but I have a few questions about it. First of all, here's what I've got right now: https://jsfiddle.net/w6kLbg2q/

(function($) {
  var canvas = document.getElementById('canvas');
  var context = canvas.getContext('2d');

  //Detect a mouse down. Set the xy coordinates
  var mouseDown = false;

  $(canvas).mousedown(function(e) {
    mouseDown = true;

    context.beginPath();
    context.moveTo(e.pageX, e.pageY);
  });

  //Detect that the mouse is moving and draw the line while the mouse is still down
  $(canvas).mousemove(function(e) {
    if (mouseDown) {
      var x = e.offsetX * 2;
      var y = e.offsetY * 2;


      context.lineTo(x, y);
      context.strokeStyle = '#000';
      context.stroke();

    }
  });

  //On mouse up, reset the coordinates
  $(canvas).mouseup(function() {
    mouseDown = false;
    context.closePath();
  });

})(jQuery);
#canvas {
  width: 400px;
  height: 400px;
  border: 1px solid;
  margin: 0 auto;
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas">
        This text is displayed if your browser does not support HTML5 Canvas.
    </canvas>

  1. How come the line is not being drawn exactly at the point of the cursor? What am I doing wrong?
  2. I'm using this e.offsetX * 2; because I saw this somewhere and it wasn't working when I did e.pageX. Why is that? And why is the * 2 necessary?

Thanks in advance!

Upvotes: 2

Views: 123

Answers (3)

Assafi Cohen-Arazi
Assafi Cohen-Arazi

Reputation: 847

I fixed it! Replace your offsets:

var x = e.offsetX / 1.325;
var y = e.offsetY / 2.65;

Why does this work?

The function in which you use to locate the mouse has a constant offset in both directions. I figured this out when I noticed that the drawing was exponentially farther away then my mouse, but was right on at (0,0) on the graph (because any offset * 0, the coordinate, equals 0). I found the constant offset of the canvas and plugged it in, and it worked! :)

I understand there are other answers, and they are probably more accurate, but it doesn't mean you have to hate on this one :(

Upvotes: -1

Blindman67
Blindman67

Reputation: 54089

Canvas resolution V display size

The problem has to do with the fact that the canvas resolutions and canvas display size do not match.

Canvas resolution is set via the canvas width and height properties. They can be set as follows

<canvas id="canvasId" width="400" height="400"></canvas>

or via script

canvasId.width = 400;
canvasId.height = 400;

If you do not set these values the canvas will default to 300 by 150.

Display size is the actual size of the canvas as displayed on the page and is set via the style properties width and height

<canvas id="canvasId" style="width:400px;height:400px;"></canvas>

or via script

 canvasId.style.width = "400px";
 canvasId.style.height = "400px";

or in CSS

 #canvasId { 
     width : 400px;
     height : 400px;
 }

Fixing your problem

There are two solutions to your problem.

First is to have the display size match the canvas resolution.

Or you can use the difference between display size and canvas resolution to calculate the scale for the mouse.

var bounds = canvasId.getBoundingClientRect()
mouseScaleX = canvasId.width / bounds.width; 
mouseScaleY = canvasId.height / bounds.height; 
// then multiply the mouse coords with scales

Code example

I have modified your snippet to scale the mouse coords to match the canvas resolution.

(function($) {
  var canvas = document.getElementById('canvas');
  var context = canvas.getContext('2d');

  //Detect a mouse down. Set the xy coordinates
  var mouseDown = false;

  $(canvas).mousedown(function(e) {
    mouseDown = true;
    var bounds = e.target.getBoundingClientRect()
    mouseScaleX = e.target.width / bounds.width; 
    mouseScaleY = e.target.height / bounds.height; 

    context.beginPath();
    context.moveTo(e.offsetX * mouseScaleX, e.offsetY * mouseScaleY);
  });

  //Detect that the mouse is moving and draw the line while the mouse is still down
  $(canvas).mousemove(function(e) {
    if (mouseDown) {
      var bounds = e.target.getBoundingClientRect()
      mouseScaleX = e.target.width / bounds.width; 
      mouseScaleY = e.target.height / bounds.height; 

      var x = e.offsetX * mouseScaleX;
      var y = e.offsetY * mouseScaleY;


      context.lineTo(x, y);
      context.strokeStyle = '#000';
      context.stroke();

    }
  });

  //On mouse up, reset the coordinates
  $(canvas).mouseup(function() {
    mouseDown = false;
    context.closePath();
  });

})(jQuery);
#canvas {
  width: 400px;
  height: 400px;
  border: 1px solid;
  margin: 0 auto;
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas">
        This text is displayed if your browser does not support HTML5 Canvas.
    </canvas>

Upvotes: 2

Mike Cluck
Mike Cluck

Reputation: 32511

There's a few problems:

  1. You're stretching out the canvas with CSS rather than setting the actual viewport size.
  2. I don't know who told you to multiply your offset by 2 but that's wrong. It's just an issue because of what I said with point 1.

Here's how you correctly fix it without magic numbers.

(function($) {
  var canvas = document.getElementById('canvas');
  var context = canvas.getContext('2d');
  var mouseDown = false;

  $(canvas).mousedown(function(e) {
    mouseDown = true;
    context.beginPath();
    
    // I'd also suggest changing from pageX/Y to offsetX/Y
    // otherwise you get this weird jumping effect
    context.moveTo(e.offsetX, e.offsetY);
  });

  $(canvas).mousemove(function(e) {
    if (mouseDown) {
      // Remove the multiplier
      var x = e.offsetX;
      var y = e.offsetY;

      context.lineTo(x, y);
      context.strokeStyle = '#000';
      context.stroke();
    }
  });

  $(canvas).mouseup(function() {
    mouseDown = false;
    context.closePath();
  });
})(jQuery);
#canvas {
  /* You don't need to set the size here */
  border: 1px solid;
  margin: 0 auto;
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<!-- Notice how I set the size of the canvas -->
<canvas id="canvas" width="400" height="400">
  This text is displayed if your browser does not support HTML5 Canvas.
</canvas>

Upvotes: 1

Related Questions