manbearpig1 manbearpig1 - 1 year ago 52
HTML Question

HTML canvas drawing application breaks on different screen sizes

This drawing application worked perfectly on my laptop. Testing on different screen sizes, the line drawn does not align with the cursor. I think that I will have to apply some scaling mechanism.

var canvas, ctx, painting = false,

function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY -
// Sender drawing function.
function drawLineImmed(x1, y1, x2, y2) {
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = 'white';
// Receiver drawing function.
function drawLineTwo(data) {
ctx.strokeStyle = 'white';
// Get draw data. Pass to receiver drawing function.
socket.on('draw', function(data) {
// Sender emit drawing data.
function mouseMove(evt) {
var mousePos = getMousePos(canvas, evt);
if (painting) {
drawLineImmed(previousMousePos.x, previousMousePos.y, mousePos.x, mousePos.y);
socket.emit('draw', {px:previousMousePos.x, py:previousMousePos.y, mx:mousePos.x, my:mousePos.y}, page);
previousMousePos = mousePos;
function clicked(evt) {
previousMousePos = getMousePos(canvas, evt);
painting = true;
function release(evt) {
painting = false;
function leave(evt) {
painting = false;
$(document).ready(function() {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
painting = false;
canvas.addEventListener('mousemove', mouseMove, false);
canvas.addEventListener('mousedown', clicked);
canvas.addEventListener('mouseup', release);
canvas.addEventListener('mouseleave', leave);

// CSS

#canvas {
border-radius: 2px;
background-color: rgb(33,37,43);
position: fixed;
left: 1.7%;
top: 3%;
border-radius: 8px;
border-style: solid;
border-width: 3px;
border-color: black;
width: 80%;

What has to scale relative to what?

Answer Source

As Blindman67 points out canvases have 2 sizes. The size that defines the number of pixels that are in the canvas (canvas.width & canvas.height) and the size the canvas is displayed which is set by CSS.

The normal way I resize is like this

function resizeCanvasToMatchDisplaySize(canvas) {

  // look up the size the canvas is displayed
  var desiredWidth = canvas.clientWidth;
  var desiredHeight = canvas.clientHeight;

  // if the number of pixels in the canvas doesn't match
  // update the canvas's content size.
  if (canvas.width != desiredWidth || canvas.height != desiredHeight) {
    canvas.width = desiredWidth;
    canvas.height = desiredHeight;

Then I call that before drawing (could be in mouseMove for example). That way it only clears the canvas if the size changed.

You can also use canvas.getBoundingClientRect() and it is technically more correct but it also generates garbage which for my purposes is usually bad. For yours though I suspect it wouldn't matter.

Anytime you set/change the size of a canvas everything about it is reset and it is cleared. That means other settings like fillStyle, lineWidth etc are all reset to defaults when you change the size. If you want something to last past resizing you'll need to record it somehow. Ideas include keeping track of everything drawn so far and drawing it again after resizing. Another idea is to copy the current canvas to another offscreen canvas using something like

 // make a new canvas if we haven't already
 offscreenCanvas = offscreenCanvas || document.createElement("canvas");

 // make the offscreen canvas match the size of the onscreen canvas
 offscreenCanvas.width = onscreenCanvas.width;
 offscreenCanvas.height = onscreenCanvas.height;

 // get a context for a offscreen canvas
 offscreenCanvasContext = offscreenCanvas.getContext("2d");

 // clear it just in case it's old and the size didn't change.
 offscreenCanvasContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);

 // copy the onscreen canvas to the offscreen canvas
 offscreenCanvasContext.drawImage(onscreenCanvas, 0, 0);

 // resize the onscreen canvas

 // copy back the old content
 onscreenCanvasContext.drawImage(offscreenCanvas, 0, 0);

 // free the memory used by the offscreen canvas
 offscreenCanvas.width = 0;
 offscreenCanvas.height = 0;

Of course if the old canvas size was larger you're going to end up clipping content.

Also if your canvas's drawingBuffer size (the number of pixels) doesn't match the display size you can do the math to make the mouse position still match