在画布上弯曲的粗箭头

时间:2014-01-10 19:38:07

标签: jquery html5 canvas

我试图在画布元素上绘制一个弯曲的粗(超过1px)箭头。但我不知道怎么做。我在画布元素上绘制了一个带有笔划的直箭头,当我添加填充选项时,它并没有像箭头那样真正锁定,因为它有错误的视角。也许有人知道如何在画布元素上绘制一个厚约5px厚的弯曲箭头。

1 个答案:

答案 0 :(得分:5)

您可以使用2个画布上下文绘制绘制弯曲箭头:

  • 轴的贝塞尔曲线
  • 箭头的三角形线。

演示:http://jsfiddle.net/m1erickson/x2dy6/

enter image description here

轴只是上下文的三次贝塞尔曲线:

ctx.moveTo(bez.sx,bez.sy);
ctx.bezierCurveTo(bez.cx1,bez.cy1,bez.cx2,bez.cy2,bez.ex,bez.ey);
ctx.lineWidth=5;
ctx.stroke();

箭头只是一个由上下文换行命令构成的三角形:

ctx.moveTo(0,0);
ctx.lineTo(0,-10);
ctx.lineTo(15,0);
ctx.lineTo(0,10);
ctx.lineTo(0,0);

使用上下文的变换(平移+旋转)将箭头定位在轴的末端:

ctx.translate(bez.ex,bez.ey);
ctx.rotate(endingAngle);

您需要曲线末端的角度才能以适当的角度旋转箭头。

可以像这样计算出正确的结束角度:

var pointNearEnd=getCubicBezierXYatT(
    {x:bez.sx,y:bez.sy},
    {x:bez.cx1,y:bez.cy1},
    {x:bez.cx2,y:bez.cy2},
    {x:bez.ex,y:bez.ey},0.99);
var dx=bez.ex-pointNearEnd.x;
var dy=bez.ey-pointNearEnd.y;
var endingAngle=Math.atan2(dy,dx);

// helper functions

function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
    var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
    var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
    return({x:x,y:y});
}

// cubic helper formula at T distance
function CubicN(T, a,b,c,d) {
    var t2 = T * T;
    var t3 = t2 * T;
    return a + (-a * 3 + T * (3 * a - a * T)) * T
    + (3 * b + T * (-6 * b + b * 3 * T)) * T
    + (c * 3 - c * 3 * T) * t2
    + d * t3;
}

以下是完整的代码示例:

<!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");

    var bez1={sx:50,sy:150,cx1:125,cy1:75,cx2:100,cy2:225,ex:200,ey:130};

    drawCurvedArrow(bez1);

    function drawCurvedArrow(bez){

        // calculate the ending angle of the curve

        var pointNearEnd=getCubicBezierXYatT(
            {x:bez.sx,y:bez.sy},
            {x:bez.cx1,y:bez.cy1},
            {x:bez.cx2,y:bez.cy2},
            {x:bez.ex,y:bez.ey},0.99);
        var dx=bez.ex-pointNearEnd.x;
        var dy=bez.ey-pointNearEnd.y;
        var endingAngle=Math.atan2(dy,dx);

        // draw the arrow shaft

        ctx.moveTo(bez.sx,bez.sy);
        ctx.bezierCurveTo(bez.cx1,bez.cy1,bez.cx2,bez.cy2,bez.ex,bez.ey);
        ctx.lineWidth=5;
        ctx.stroke();

        // draw the arrow head

        var size=ctx.lineWidth;

        ctx.beginPath();
        ctx.save();
        ctx.translate(bez.ex,bez.ey);
        ctx.rotate(endingAngle);
        ctx.moveTo(0,0);
        ctx.lineTo(0,-size*2);
        ctx.lineTo(size*3,0);
        ctx.lineTo(0,size*2);
        ctx.lineTo(0,0);
        ctx.closePath();
        ctx.fill();
        ctx.restore();

    }

    // helper functions

    function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
        var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
        var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
        return({x:x,y:y});
    }

    // cubic helper formula at T distance
    function CubicN(T, a,b,c,d) {
        var t2 = T * T;
        var t3 = t2 * T;
        return a + (-a * 3 + T * (3 * a - a * T)) * T
        + (3 * b + T * (-6 * b + b * 3 * T)) * T
        + (c * 3 - c * 3 * T) * t2
        + d * t3;
    }


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

</head>

<body>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>