今天记录分享下如何用canvas绘制带有箭头的简单思路。举一个如下图所示的箭头为例来说吧~
要绘制这样一个箭头,第一步我们要先绘制一条线段,然后在线段的末端绘制箭头,需要算出箭头两端所在点的位置,那么这两个点要怎么计算呢?其实不难,就纯数学计算,算出两个点和线段的夹角就可以了,参考下图:
假设线段与坐标系x轴的夹角为α,箭头与线的夹角为θ, 根据三角函数关系很容易就得出如下关系:
// 箭头向上
// 指向左侧
/Lpoint=(x1-r*cos(π-(α+θ), y + r*sin(π-(α+θ))) => (x1+r*cos(α+θ), y + r*sin(α+θ))
/Rpoint=(x1+r*cos(α-θ), y + r*sin(α-θ)) => (x1+r*cos(α-θ), y + r*sin(α-θ))
// 指向右侧
/Lpoint=(x1- r*cos(π - (α+θ)), y + r*sin(π - (α+θ))) => (x1+r*cos(α+θ), y + r*sin(α+θ))
Rpoint=(x1+r*cos(α-θ), y + r*sin(α-θ)) => (x1+r*cos(α-θ), y + r*sin(α-θ))
// 箭头向下
指向左侧
Lpoint=(x1-r*cos(π-(α+θ)), y1 - r*sin(π-(α+θ))) => (x1+r*cos(α+θ), y - r*sin(α+θ))
Rpoint=(x1+ r*cos(α-θ), y - r*sin(α-θ)) => (x1+r*cos(α-θ), y - r*sin(α-θ))
// 指向右侧
Lpoint=(x1-r*cos(π-(α+θ), y - r*sin(π-(α+θ))) => (x1+r*cos(α+θ), y - r*sin(α+θ))
Rpoint=(x1+r*cos(α-θ), y - r*sin(α-θ)) => (x1+r*cos(α-θ), y - r*sin(α-θ))
整合为代码则是:
/**
* 绘制带有箭头的直线
* @param fromX/fromY 起点坐标
* @param toX/toY 终点坐标
* @param color 线与箭头颜色
**/
function drawLineArrow(ctx, fromX, fromY, toX, toY, color) {
const headLenth = 10;//自定义箭头线的长度r
const theta = 35;//箭头与线的夹角θ
let arrowX, arrowY;//箭头线终点坐标
// 计算各角度和对应的箭头终点坐标
const angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI;
const angleLeft = (angle + theta) * Math.PI / 180;
const angleRight = (angle - theta) * Math.PI / 180;
const LpointX = headLenth * Math.cos(angleLeft);
const LpointY = headLenth * Math.sin(angleLeft);
const RpointX = headLenth * Math.cos(angleRight);
const RpointY = headLenth * Math.sin(angleRight);
ctx.beginPath();
//画直线
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
arrowX = toX + LpointX;
arrowY = toY + LpointY;
//画上边箭头线
ctx.moveTo(arrowX, arrowY);
ctx.lineTo(toX, toY);
arrowX = toX + RpointX;
arrowY = toY + RpointY;
//画下边箭头线
ctx.lineTo(arrowX, arrowY);
ctx.strokeStyle = color;
ctx.stroke();
}
到这,绘制以一个箭头好像也不难嘛,在这个基础上做更多的可配置和定义,其实能绘制出更多不形态的箭头。
完整的demo代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绘制箭头</title>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
</body>
<script>
canvas = document.getElementById("canvas");
ctx = canvas.getContext('2d');
drawLineArrow(ctx, 300, 200, 200, 100, "#3D7BFF");
/**
* 绘制带有箭头的直线
* @param fromX/fromY 起点坐标
* @param toX/toY 终点坐标
* @param color 线与箭头颜色
**/
function drawLineArrow(ctx, fromX, fromY, toX, toY, color) {
const headLenth = 10;//自定义箭头线的长度r
const theta = 35;//箭头与线的夹角θ
let arrowX, arrowY;//箭头线终点坐标
// 计算各角度和对应的箭头终点坐标
const angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI;
const angleLeft = (angle + theta) * Math.PI / 180;
const angleRight = (angle - theta) * Math.PI / 180;
const LpointX = headLenth * Math.cos(angleLeft);
const LpointY = headLenth * Math.sin(angleLeft);
const RpointX = headLenth * Math.cos(angleRight);
const RpointY = headLenth * Math.sin(angleRight);
ctx.beginPath();
//画直线
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
arrowX = toX + LpointX;
arrowY = toY + LpointY;
//画上边箭头线
ctx.moveTo(arrowX, arrowY);
ctx.lineTo(toX, toY);
arrowX = toX + RpointX;
arrowY = toY + RpointY;
//画下边箭头线
ctx.lineTo(arrowX, arrowY);
ctx.strokeStyle = color;
ctx.stroke();
}
</script>
</html>