Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mouse position within HTML 5 responsive Canvas

I've searched through other questions but none of them fit my case.

I have a canvas element:

<canvas id="linear-synoptic-map" width="1053px" height="1000px" ng-click="linearSynopticCtrl.canvasClicked($event)" ng-mousemove="linearSynopticCtrl.mouseMovedOverCanvas($event)">
</canvas>

And I'm getting the position with this function:

linearSynopticCtrl.getPositionFromEvent = function (event) {
  var rect = linearSynopticCtrl.canvas.getBoundingClientRect();
  var x = event.x - rect.left;
  var y = event.y - rect.top;
  return new Point(x,y);
};

The problem is that the canvas needs to be responsive so I've added the following css rule:

canvas#linear-synoptic-map {
  width: 100%;
}

When the resizing happens (when the canvas dimension is shrinked over definition or enlarged over definition: 1053x1000) a gap starts to show between the correct mouse position and the returned position by the function.

I have also tried obtaining the position with this approach:

linearSynopticCtrl.getPositionFromEvent = function (event) {
  var x = event.x - linearSynopticCtrl.canvas.offsetLeft;
  var y = event.y - linearSynopticCtrl.canvas.offsetTop;
  return new Point(x,y);
};

But I get much worse results.

Does anyone know how to fix this problem?

like image 502
tomascharad Avatar asked Feb 20 '15 12:02

tomascharad


1 Answers

Mouse coordinates are never scaled.

Therefore, you must scale the mouse coordinates for them to reflect the scaled canvas.

Here's one way to make your canvas responsive and get scaled mouse coordinates:

// handle responsively resizing the canvas   
var scale=1.00;
var originalWindowWidth=window.innerWidth;
var originalCanvasWidth=document.getElementById('canvas').width;
function debounce(func, wait, immediate) {
  var timeout;
  return function() {
    var context = this, args = arguments;
    var later = function() {
      timeout = null;
      if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
};
var resizeCanvas = debounce(function() {
  scale=window.innerWidth/originalWindowWidth;
  $('#canvas').css('width',originalCanvasWidth*scale);
}, 250);
window.addEventListener('resize', resizeCanvas);

// now, do normal app stuff

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

$("#canvas").mousemove(function(e){handleMouseMove(e);});

ctx.fillRect(50,50,100,100);
ctx.fillText('Rect drawn at [50,50]',50,35);

function handleMouseMove(e){

  var rect = canvas.getBoundingClientRect();
  var x = parseInt((event.x - rect.left)/scale);
  var y = parseInt((event.y - rect.top)/scale);

  $('#mouse').text(x+'/'+y);

}
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Mouse will report rect corner at<br>[50,50] even after resizing window</h4>
<h4 id=mouse>mouse</h4>
<canvas id="canvas" width=300 height=300></canvas>
like image 109
markE Avatar answered Sep 29 '22 09:09

markE