0
votes

I am trying to include a canvas element over a dynamically sized video that will load asynchronously. On the canvas, the user will be able to drag and resize a rectangle selection box.

In my JS file, I have a listener watching the window and resizing the canvas via the canvas element's .width and .height properties to be the exact width and height of the video.

For the JS rectangular select code, I am following the fiddle from the first answer to this StackOverflow question: drawing a rectangle with mouse click and drag - javascript, however, for some reason, when the user is scrolled down on the page drawing on the canvas is completely distorted, and rectangles start being drawn pretty far away from the mouse. When the user is at the top of the page, however, the rectangle draws at the correct position. Why would this happen?

Below is some of the JS code I am using:

  
// Happens on mousedown event
  downCanvas: function(e) {
    $('.label-grid-canvas').css('cursor','crosshair');
    this.isDrawing = true
    this.startX = parseInt(e.clientX - this.offsetX);
    this.startY = parseInt(e.clientY - this.offsetY);
  },
  
// Happens on mouseup event
  upCanvas: function(e) {
    this.isDrawing = false;
    $('.label-grid-canvas').css('cursor','default');
  },
  // Happens on mousemove event
  moveCanvas: function(e) {
    if (this.isDrawing) {
      var mouseX = parseInt(e.clientX - this.offsetX);
      var mouseY = parseInt(e.clientY - this.offsetY);

      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.ctx.beginPath();
      this.ctx.rect(this.startX, this.startY, mouseX - this.startX, mouseY - this.startY);
      this.ctx.stroke();
    }
  },

  resizeCanvas: function() {
    this.canvas.width = $('#video-canvas')[0].offsetWidth;
    this.canvas.height = $('#video-canvas')[0].offsetHeight;

    this.canvasOffset = $('.label-grid-canvas').offset();
    this.offsetX = this.canvasOffset.left;
    this.offsetY = this.canvasOffset.top;

    this.redraw();
  },

  redraw: function() {
    this.ctx.strokeStyle = 'blue';
    this.ctx.lineWidth = '2';
    this.ctx.strokeRect(0, 0, $('#video-canvas')[0].offsetWidth,
        $('#video-canvas')[0].offsetHeight);
  },

// Happens after page-load
  initialize: function(options) {
    this.canvas = document.getElementById('label-grid-canvas');
    this.isDrawing = false;
    this.ctx = this.canvas.getContext('2d');
    this.startX;
    this.startY;
    this.canvasOffset = $('.label-grid-canvas').offset();
    this.offsetX = this.canvasOffset.left;
    this.offsetY = this.canvasOffset.top;
    $(window).on('resize', _.bind(this.resizeCanvas, this));
 ....
}

Please disregard the inefficiencies, I am just first trying to hack a working thing together before cleaning it up.

1
This is a common problem raised on Stackoverflow. Don't resize html5 canvas with CSS. Instead, resize the canvas element itself: canvasElement.width=800; canvasElement.height=500;. You can use the extended form of context.drawImage to enlarge a smaller video image onto a larger canvas. This way you have no distortion and no misreported mouse coordinates.markE
@markE thanks for the advice. So now what I have my JS doing is watching the window for a resize and then updating the canvas' dimensions directly using your methods. Now the rectangle is a bit more accurate, but it is still definitely off. Any advice?Zach
Is your user allowed to scroll and are you accounting for that? BTW, your question is almost identical to this one. Are you both working on the same project? Anyway, check out the other Q&A which has a demo including resizable/scrollable design.markE
Yes and no I'm not accounting for that, and I just realized the scroll position is my issue, now that's what I need to fix. And that's pretty funny, but no that person isn't me.Zach
I've added an answer showing how to account for resizing and scrolling when calculating mouse position.markE

1 Answers

2
votes

Here's skeleton showing how to account for resizing and scrolling when calculating mouse position.

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }

var isDown=false;
var startX,startY,mouseX,mouseY;

var $mouse=$('#mouse');
$("#canvas").mousemove(function(e){handleMouseMove(e);});

function handleMouseMove(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  // calc the current mouse position
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  // report the mouse position
  $mouse.text('Mouse position: '+mouseX+' / '+mouseY);
}
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 id=mouse>Move the mouse around the canvas.</h4>
<canvas id="canvas" width=300 height=300></canvas>