我试图在画布元素上绘制一个弯曲的粗(超过1px)箭头。但我不知道怎么做。我在画布元素上绘制了一个带有笔划的直箭头,当我添加填充选项时,它并没有像箭头那样真正锁定,因为它有错误的视角。也许有人知道如何在画布元素上绘制一个厚约5px厚的弯曲箭头。
答案 0 :(得分:5)
您可以使用2个画布上下文绘制绘制弯曲箭头:
演示:http://jsfiddle.net/m1erickson/x2dy6/
轴只是上下文的三次贝塞尔曲线:
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>