2

Please take a look at this little example. The clickhandler only works if you click in the middle of the line. It seems that the method isPointInPath does not consider the width of the line. Is there a way to solve this problem?

Maxii
  • 695
  • 2
  • 13
  • 26

1 Answers1

3

Yes, you are correct.

The new isPointInPath() works only on the centerline of a "fat" line--not the full width of the line.

It's more user friendly on closed shapes that are more than 1 pixel wide ;)

A Workaround for your exact question: Instead of drawing a fat line, draw a 20px wide rectangle.

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/QyWDY/

This code uses basic trigonometry to create a rectangle around a line. In the mousedown event handler, it redraws the rectangle transparently and then tests isPointInPath().

If you need to test a poly-line, you can use these same principles to make rectangle-lines for each segment of your poly-line.

<!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>

<style>
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    // get canvas's relative position
    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

    // line specifications
    var x1=50;
    var y1=50;
    var x2=300;
    var y2=100;

    // draw the lineRectangle
    var lineRect=defineLineAsRect(x1,y1,x2,y2,20);
    drawLineAsRect(lineRect,"black");


    // overlay the line (just as visual proof)
    drawLine(x1,y1,x2,y2,3,"red");


    function drawLine(x1,y1,x2,y2,lineWidth,color){
        ctx.fillStyle=color;
        ctx.strokeStyle=color;
        ctx.lineWidth=lineWidth;
        ctx.save();
        ctx.beginPath();
        ctx.moveTo(x1,y1);
        ctx.lineTo(x2,y2);
        ctx.stroke();
        ctx.restore();
    }


    function drawLineAsRect(lineAsRect,color){
        var r=lineAsRect;
        ctx.save();
        ctx.beginPath();
        ctx.translate(r.translateX,r.translateY);
        ctx.rotate(r.rotation);
        ctx.rect(r.rectX,r.rectY,r.rectWidth,r.rectHeight);
        ctx.translate(-r.translateX,-r.translateY);
        ctx.rotate(-r.rotation);
        ctx.fillStyle=color;
        ctx.strokeStyle=color;
        ctx.fill();
        ctx.stroke();
        ctx.restore();
    }


    function defineLineAsRect(x1,y1,x2,y2,lineWidth){
        var dx=x2-x1; // deltaX used in length and angle calculations 
        var dy=y2-y1; // deltaY used in length and angle calculations
        var lineLength= Math.sqrt(dx*dx+dy*dy);
        var lineRadianAngle=Math.atan2(dy,dx);

        return({
            translateX:x1,
            translateY:y1,
            rotation:lineRadianAngle,
            rectX:0,
            rectY:-lineWidth/2,
            rectWidth:lineLength,
            rectHeight:lineWidth
        });
    }


    function handleMouseDown(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // draw our lineRect
      drawLineAsRect(lineRect,"transparent");

      // test if hit in the lineRect
      if(ctx.isPointInPath(mouseX,mouseY)){
              alert('Yes');
      }
    }

    canvas.addEventListener("mousedown", handleMouseDown, false);

}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=310 height=115></canvas>
</body>
</html>
markE
  • 102,905
  • 11
  • 164
  • 176
  • Do you have an example for drawing a rectangle instead of a line? I think it's quite difficult to do this if you want to create the lines by mousemoves and you don't know the line positions before. – Maxii Mar 23 '13 at 20:43
  • Edited my answer to include an example for you :) – markE Mar 26 '13 at 18:12