Html 5 Canvas complete arrowhead

前端 未结 5 1407
鱼传尺愫
鱼传尺愫 2021-01-14 08:47

I\'m using the wPaint plugin and I am attempting to add a few more features. What I need is a drawn line to end with an \"arrowhead\". I have tried just about everything I c

相关标签:
5条回答
  • 2021-01-14 08:58

    As addition to markE's answer combined with user1707810 comment:

    Both blocks of (start/end radians):

     - ((this.x2 > this.x1)?-90:90)*Math.PI/180;
    

    should be changed to :

     - ((this.x2 >= this.x1)?-90:90)*Math.PI/180;
    
    0 讨论(0)
  • 2021-01-14 09:03

    This is how to create a line object that draws arrowheads on both ends

    The interesting part is calculating the angle of the arrowheads like this:

    var startRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
    startRadians+=((this.x2>=this.x1)?-90:90)*Math.PI/180;
    
    var endRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
    endRadians+=((this.x2>=this.x1)?90:-90)*Math.PI/180;
    

    The rest is just drawing the line and 2 triangles for arrowheads the calculated rotations

    Line.prototype.drawArrowhead=function(ctx,x,y,radians){
        ctx.save();
        ctx.beginPath();
        ctx.translate(x,y);
        ctx.rotate(radians);
        ctx.moveTo(0,0);
        ctx.lineTo(5,20);
        ctx.lineTo(-5,20);
        ctx.closePath();
        ctx.restore();
        ctx.fill();
    }
    

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

    <!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 context=canvas.getContext("2d");
    
        function Line(x1,y1,x2,y2){
            this.x1=x1;
            this.y1=y1;
            this.x2=x2;
            this.y2=y2;
        }
        Line.prototype.drawWithArrowheads=function(ctx){
    
            // arbitrary styling
            ctx.strokeStyle="blue";
            ctx.fillStyle="blue";
            ctx.lineWidth=1;
    
            // draw the line
            ctx.beginPath();
            ctx.moveTo(this.x1,this.y1);
            ctx.lineTo(this.x2,this.y2);
            ctx.stroke();
    
            // draw the starting arrowhead
            var startRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
            startRadians+=((this.x2>this.x1)?-90:90)*Math.PI/180;
            this.drawArrowhead(ctx,this.x1,this.y1,startRadians);
            // draw the ending arrowhead
            var endRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
            endRadians+=((this.x2>this.x1)?90:-90)*Math.PI/180;
            this.drawArrowhead(ctx,this.x2,this.y2,endRadians);
    
    
        }
        Line.prototype.drawArrowhead=function(ctx,x,y,radians){
            ctx.save();
            ctx.beginPath();
            ctx.translate(x,y);
            ctx.rotate(radians);
            ctx.moveTo(0,0);
            ctx.lineTo(5,20);
            ctx.lineTo(-5,20);
            ctx.closePath();
            ctx.restore();
            ctx.fill();
        }
    
        // create a new line object
        var line=new Line(50,50,150,150);
        // draw the line
        line.drawWithArrowheads(context);
    
    }); // end $(function(){});
    </script>
    
    </head>
    
    <body>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>
    
    0 讨论(0)
  • 2021-01-14 09:19

    This goes wrong for vertical lines. Try

    var line=new Line(50,50,50,275)

    0 讨论(0)
  • 2021-01-14 09:20

    Simpler version

    key difference. Using Math.atan2 removes the need for if

    This one also puts the arrowheads at the ends of the line rather than past the end of the line

    In other words

    this

    start         end
        |<------->|
    

    vs this

       <|---------|>
    

    function arrow(ctx, x1, y1, x2, y2, start, end) {
      var rot = -Math.atan2(x1 - x2, y1 - y2);
      ctx.beginPath();
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.stroke();
      if (start) {
        arrowHead(x1, y1, rot);
      }
      if (end) {
        arrowHead(x2, y2, rot + Math.PI);
      }
    }
    
    function arrowHead(x, y, rot) {
      ctx.save();
      ctx.translate(x, y);
      ctx.rotate(rot);
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(-5, -12);
      ctx.lineTo(5, -12);
      ctx.closePath();
      ctx.fill();
      ctx.restore();
    }
    
    // test it -------
    
    var ctx = document.createElement("canvas").getContext("2d");
    document.body.appendChild(ctx.canvas);
    
    
    // draw some arrows
    ctx.save();
    ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
    
    for (var ii = 0; ii <= 12; ++ii) {
      var u = ii / 12;
      var color = hsl(u * 360, 1, 0.5);
      ctx.fillStyle = color;
      ctx.strokeStyle = color;
      var a = u * Math.PI;
      var x = Math.cos(a) * 120;
      var y = Math.sin(a) * 70;
      arrow(ctx, -x, -y, x, y, true, true);
      
      // draw the end points to see the arrowheads match
      ctx.fillStyle = "#000";
      ctx.fillRect(-x - 1, -y - 1, 3, 3);
      ctx.fillRect( x - 1,  y - 1, 3, 3);
    }
    
    ctx.restore();
    
    function hsl(h, s, l) {
      return `hsl(${h},${s * 100}%,${l * 100}%)`;
    }
    canvas { border: 1px solid black; }

    One problem with the above solution is if you thicken the line stroke it would poke through the arrowhead. It's not hard to fix but you'd have to compute the length of the line in pixels, then subtract the size of the arrowhead from both sides.

    something like this

    function arrow(ctx, x1, y1, x2, y2, start, end) {
      var dx = x2 - x1;
      var dy = y2 - y1;
      var rot = -Math.atan2(dx, dy);
      var len = Math.sqrt(dx * dx + dy * dy);
      var arrowHeadLen = 10;
      ctx.save();
      ctx.translate(x1, y1);
      ctx.rotate(rot);
      ctx.beginPath();    
      ctx.moveTo(0, start ? arrowHeadLen : 0);
      ctx.lineTo(0, len - (end ? arrowHeadLen : 0));
      ctx.stroke();
      if (end) {
        ctx.save();
        ctx.translate(0, len);
        arrowHead(ctx);
        ctx.restore();
      }
      if (start) {
        ctx.rotate(Math.PI);
        arrowHead(ctx);
      }
      ctx.restore();
    }
    
    function arrowHead(ctx) {
      ctx.beginPath();
      ctx.moveTo(0, 0);
      ctx.lineTo(-5, -12);
      ctx.lineTo(5, -12);
      ctx.closePath();
      ctx.fill();
    }
    
    // test it -------
    
    var ctx = document.createElement("canvas").getContext("2d");
    document.body.appendChild(ctx.canvas);
    
    
    // draw some arrows
    ctx.save();
    ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
    
    for (var ii = 0; ii < 12; ++ii) {
      var u = ii / 12;
      var color = hsl(u * 360, 1, 0.5);
      ctx.fillStyle = color;
      ctx.strokeStyle = color;
      var a = u * Math.PI;
      var x = Math.cos(a) * 120;
      var y = Math.sin(a) * 70;
      arrow(ctx, -x, -y, x, y, true, true);
      ctx.fillStyle = "#000";  // mark the ends so we can see where they are
      ctx.fillRect(-x - 1, -y - 1, 3, 3);
      ctx.fillRect( x - 1,  y - 1, 3, 3);
    }
    
    ctx.restore();
    
    function hsl(h, s, l) {
      return `hsl(${h},${s * 100}%,${l * 100}%)`;
    }
    canvas { border: 1px solid black; }

    In other words the first solution draws arrows like this

    Where as the second solution draws arrows like this

    0 讨论(0)
  • 2021-01-14 09:22

    my simple solution

    ctx.beginPath(); 
    ctx.moveTo(ax,ay);
    ctx.lineTo(bx,by);
    ctx.stroke();
    ctx.closePath();
    angle=Math.PI+Math.atan2(by-ay,bx-ax);
    angle1=angle+Math.PI/6;
    angle2=angle-Math.PI/6;
    ctx.beginPath(); 
    ctx.moveTo(bx,by);
    ctx.arc(bx,by,5,angle1,angle2,true);
    ctx.lineTo(bx,by);
    ctx.fill();
    ctx.closePath();
    
    0 讨论(0)
提交回复
热议问题