javascripthtmlhtml5-canvasmouseovermouseout

Detect if Mouse is over an object inside canvas


I have created a line inside a canvas element. I am looking for the easiest way to detect if the position of the mouse is inside the line, which is inside the canvas.

I have used this function to see the position of the mouse inside the canvas, but I am very confused on how I should proceed.

function getMousePos(c, evt) {
            var rect = c.getBoundingClientRect();
            return {
                x: evt.clientX - rect.left,
                y: evt.clientY - rect.top
            };
        }

I have also looked at this topic Fabricjs detect mouse over object path , but it detects if the mouse is inside the canvas, not inside the object.

The line that I create is a part of smaller lines, connected to each other.

 for (var i = 0; i < 140 ; i++) {

                ctx.beginPath();

                ctx.moveTo(x[i],y[i]);
                ctx.quadraticCurveTo(x[i],50,x[i+1],y[i+1]);
                ctx.lineWidth = 40;

                ctx.strokeStyle = 'white';
                ctx.lineCap = 'round';
                ctx.stroke();

            }

where x[i] and y[i] are the arrays with the coordinates that I want.

I hope my question is clear, although I am not very familiar with javascript.

Thanks Dimitra


Solution

  • A Demo: http://jsfiddle.net/m1erickson/Cw4ZN/

    enter image description hereenter image description here

    You need these concepts to check if the mouse is inside a line:

    Here's annotated example code for you to learn from.

    $(function() {
    
      // canvas related variables
      var canvas = document.getElementById("canvas");
      var ctx = canvas.getContext("2d");
      var $canvas = $("#canvas");
      var canvasOffset = $canvas.offset();
      var offsetX = canvasOffset.left;
      var offsetY = canvasOffset.top;
    
      // dom element to indicate if mouse is inside/outside line
      var $hit = $("#hit");
    
      // determine how close the mouse must be to the line
      // for the mouse to be inside the line
      var tolerance = 5;
    
      // define the starting & ending points of the line
      var line = {
        x0: 50,
        y0: 50,
        x1: 100,
        y1: 100
      };
    
      // set the fillstyle of the canvas
      ctx.fillStyle = "red";
    
      // draw the line for the first time
      draw(line);
    
      // function to draw the line
      // and optionally draw a dot when the mouse is inside
      function draw(line, mouseX, mouseY, lineX, lineY) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.beginPath();
        ctx.moveTo(line.x0, line.y0);
        ctx.lineTo(line.x1, line.y1);
        ctx.stroke();
        if (mouseX && lineX) {
          ctx.beginPath();
          ctx.arc(lineX, lineY, tolerance, 0, Math.PI * 2);
          ctx.closePath();
          ctx.fill();
        }
      }
    
      // calculate the point on the line that's 
      // nearest to the mouse position
      function linepointNearestMouse(line, x, y) {
        //
        lerp = function(a, b, x) {
          return (a + x * (b - a));
        };
        var dx = line.x1 - line.x0;
        var dy = line.y1 - line.y0;
        var t = ((x - line.x0) * dx + (y - line.y0) * dy) / (dx * dx + dy * dy);
        var lineX = lerp(line.x0, line.x1, t);
        var lineY = lerp(line.y0, line.y1, t);
        return ({
          x: lineX,
          y: lineY
        });
      };
    
      // handle mousemove events
      // calculate how close the mouse is to the line
      // if that distance is less than tolerance then
      // display a dot on the line
      function handleMousemove(e) {
        e.preventDefault();
        e.stopPropagation();
        mouseX = parseInt(e.clientX - offsetX);
        mouseY = parseInt(e.clientY - offsetY);
        if (mouseX < line.x0 || mouseX > line.x1) {
          $hit.text("Outside");
          draw(line);
          return;
        }
        var linepoint = linepointNearestMouse(line, mouseX, mouseY);
        var dx = mouseX - linepoint.x;
        var dy = mouseY - linepoint.y;
        var distance = Math.abs(Math.sqrt(dx * dx + dy * dy));
        if (distance < tolerance) {
          $hit.text("Inside the line");
          draw(line, mouseX, mouseY, linepoint.x, linepoint.y);
        } else {
          $hit.text("Outside");
          draw(line);
        }
      }
    
      // tell the browser to call handleMousedown
      // whenever the mouse moves
      $("#canvas").mousemove(function(e) {
        handleMousemove(e);
      });
    
    }); // end $(function(){});
    body {
      background-color: ivory;
    }
    
    canvas {
      border: 1px solid red;
    }
    <!doctype html>
    <html>
    
    <head>
      <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" />
      <!-- reset css -->
      <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    
    </head>
    
    <body>
      <h2 id="hit">Move mouse near line</h2>
      <canvas id="canvas" width=300 height=300></canvas>
    </body>
    
    </html>

    About hit-testing Paths:

    If you create Paths using path commands you can use context.isPointInPath(mouseX,mouseY) to check if the mouse is inside a path. context.isPointInPath does not work well with lines however because lines theoretically have zero width to "hit".