2

I have the following code that will allow me to draw pictures on a canvas, similar to MS Paint. So, once the user presses down on the left mouse button he/she can move the mouse across the canvas to draw a picture. Drawing stops once the user lifts their finger off of the mouse button.

var colors = document.querySelectorAll('#colors div');
var link = document.getElementById('download');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var drawing = false;
var prev;
document.getElementById('current').style.backgroundColor = context.strokeStyle;
for (var i = 0; i < colors.length; i++) {
  colors[i].addEventListener('click', function(e) {
    var src = e.srcElement;
    var color = src.style.backgroundColor;
    context.strokeStyle = color;
    document.getElementById('current').style.backgroundColor = color;
  });
  colors[i].style.height = 100 / colors.length + '%';
  colors[i].style.backgroundColor = colors[i].id;
}
function clearCanvas() {
  context.clearRect(0, 0, canvas.width, canvas.height);
}
link.addEventListener('click', function() {
  link.href = canvas.toDataURL();
  link.download = 'image.png';
});
canvas.addEventListener('mousedown', function(e) {
  drawing = true;
});
canvas.addEventListener('mousemove', function(e) {
  if (drawing) {
    var coord = {
      'x': e.clientX - this.offsetLeft,
      'y': e.clientY - this.offsetTop
    };
    if (prev !== undefined) {
      context.beginPath();
      context.moveTo(prev.x, prev.y);
      context.lineTo(coord.x, coord.y);
      context.stroke();
    }
    prev = coord;
  }
});
canvas.addEventListener('mouseup', function() {
  drawing = false;
  prev = undefined;
});
#draw {
  border: 2px outset black;
  border-radius: 5px;
  display: inline-block;
  margin: 5px;
  padding: 10px;
}
#container {
  border: 2px solid black;
  width: 300px;
  height: 300px;
  position: relative;
}
#canvas {
  background-color: lightgrey;
}
#colors {
  float: right;
  position: relative;
  width: 20%;
  height: 100%;
}
#colors div {
  width: 100%
}
#buttons {
  width: 100%;
  height: auto;
}
#buttons * {
  width: 33%;
  background-color: lightgrey;
  margin-top: 5px;
  margin-left: 5px;
  display: inline;
}
#download {
  text-align: center;
  border-radius: 2px;
  padding-left: 5px;
  padding-right: 5px;
  border: 2px outset lightgrey;
  font-family: 'Arial';
  background-color: lightgrey;
  font-size: 10pt;
  cursor: context-menu;
}
#download:link {
  text-decoration: none;
  color: black;
}
#download:active {
  border: 2px inset #00acff;
  border-radius: 2px;
}
#current {
  height: 100%;
  width: 10%;
  border: 1px solid black;
  display: inline;
  position: relative;
  margin-left: 25px;
}
<div id='draw'>
  <div id='container'>
    <canvas id='canvas' width='240' height='300'></canvas>
    <div id='colors'>
      <div id='red'></div>
      <div id='blue'></div>
      <div id='green'></div>
      <div id='black'></div>
      <div id='yellow'></div>
      <div id='cyan'></div>
      <div id='magenta'></div>
    </div>
    <div id='currentColor'></div>
  </div>
  <div id='buttons'>
    <input type='button' id='clear' value='Clear Canvas' onclick='clearCanvas()' />
    <a id='download'>Download Image</a>
    <input type='button' id='current' />
  </div>
</div>

http://jsfiddle.net/SirToadstool/eywraw8t/341261/

Now while this code works within JSFiddle, testing it outside has lines that the user draws being of different coordinates than that of the mouse pointer.

I have a feeling that the CSS I have included within the code may be messing up how the coordinates get calculated in the Javascript, but I'm unsure of what to change to compensate, and have tried using clientX/Y and screenX/Y for coordinates instead of just event.x/event.y.

Any suggestions as to how I can correct calculating the coordinates when drawing?

Community
  • 1
  • 1
Delfino
  • 967
  • 4
  • 21
  • 46
  • Interestingly, in the snippet, it has the buggy behavior. your code seems straightforward, if no one answers I'll look into it when I have the time. – yuvi Sep 06 '18 at 20:26
  • this answer can help https://stackoverflow.com/a/8398189/1309377 specifically the findxy function. – Andrew Lohr Sep 06 '18 at 20:40
  • On my laptop, which has a resolution of `1920 x 1080` I was able to remedy the issue by modifying `coord` to the following: `var coord = { 'x': event.clientX - this.offsetLeft - 25, 'y': event.clientY - this.offsetTop - 25`, though I'm not entirely sure what the `25` relates to, because I'm sure it relates to something! – Delfino Sep 07 '18 at 00:46

2 Answers2

1

this.offsetLeft is equal to 0. That's why subtracting from it does nothing, because in the scope of the function, "this" is the canvas itself. Instead of clientX\Y, you want to use the mouse event offsetX\Y, so simply, this:

var coord = { 'x': e.offsetX, 'y': e.offsetY };

working example

yuvi
  • 18,155
  • 8
  • 56
  • 93
  • What exactly does `offset` mean in this case? – Delfino Sep 07 '18 at 19:22
  • 1
    @Delfino quote from link: *"provides the offset in the X coordinate of the mouse pointer between that event and the padding edge of the target node. "*. – yuvi Sep 08 '18 at 15:30
0

canvas.width is the width of the drawing canvas, this is not necessarily the width of the canvas element. This means that the canvas context can have a different resolution than the screen. So you need to adjust your coordinates accordingly. i.e.

var coord = {
  'x': (e.clientX - this.offsetLeft)*this.width/this.clientWidth,
  'y': (e.clientY - this.offsetTop)*this.height/this.clientHeight
};
Burhan B
  • 130
  • 8