Search code examples
html5-canvasmouse-cursor

Change cursor depending on section of canvas


I have a canvas and I want to change the users cursor (like style cursor pointer crosshair move etc).

Is it possible to change the users cursor while its over a certain area of my canvas without introducing "hit boxes" with style of cursor on these hit boxes?


Solution

  • Shapes drawn on the canvas do not individually receive mouse events, so individual shapes cannot receive hover events.

    A shape drawn on the canvas can be represented as a set of path commands

    A Shape == A set of path commands.
    
    // Example: A set of path commands drawing a triangle
    context.beginPath();
    context.moveTo(50,50);
    context.lineTo(75,100);
    context.lineTo(25,100);
    context.closePath();
    

    To change cursors when hovering over individual shapes you must do mouse hit-testing versus each shape (versus each path).

    You can hit-test a shape (a path) using the isPointInPath method.

    To use isPointInPath you must re-issue the path command for a shape (but no need to stroke or fill) and then call isPointInPath with the current mouse coordinates:

    // first re-issue the path commands for the shape being tested
    // and then test if the mouse is inside the shape using isPointInPath
    if( context.isPointInPath(mouseX,mouseY) ){
        alert('The mouse is inside this shape');
    }
    

    Here's example code and a Demo:

    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;
    
    var cursors=['default','w-resize','n-resize'];
    var currentCursor=0;
    
    var shapes=[];
    shapes.push({
      points:[{x:20,y:50},{x:100,y:10},{x:180,y:50},{x:100,y:90}],
      cursor:1,
    });
    shapes.push({
      points:[{x:200,y:50},{x:250,y:150},{x:200,y:250},{x:150,y:150}],
      cursor:2,
    });
    
    for(var i=0;i<shapes.length;i++){
      var s=shapes[i];
      definePath(s.points);
      ctx.stroke();
    }
    
    
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    
    
    function definePath(p){
      ctx.beginPath();
      ctx.moveTo(p[0].x,p[0].y);
      for(var i=1;i<p.length;i++){
        ctx.lineTo(p[i].x,p[i].y);
      }
      ctx.closePath();
    }
    
    function handleMouseMove(e){
      // tell the browser we're handling this event
      e.preventDefault();
      e.stopPropagation();
    
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);
    
      // Put your mousemove stuff here
      var newCursor;
      for(var i=0;i<shapes.length;i++){
        var s=shapes[i];
        definePath(s.points);
        if(ctx.isPointInPath(mouseX,mouseY)){
          newCursor=s.cursor;
          break;
        }
      }
      if(!newCursor){
        if(currentCursor>0){
          currentCursor=0;
          canvas.style.cursor=cursors[currentCursor];              
        }
      }else if(!newCursor==currentCursor){
        currentCursor=newCursor;
        canvas.style.cursor=cursors[currentCursor];              
      }
    }
    body{ background-color: ivory; }
    #canvas{border:1px solid red; margin:0 auto; }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <h4>Move the mouse over the shapes and the cursor will change.</h4>
    <canvas id="canvas" width=300 height=300></canvas>