Posted at

Canvas で矢印を描く

More than 1 year has passed since last update.


はじめに

Canvas である程度汎用的にいろいろな矢印を描くライブラリを作ったので紹介します。


デモ&ライブラリ

最終的にこんなかんじでいろいろな形状の矢印を描くことができるようになりました。

image


使い方


example.html

<script src="https://frogcat.github.io/canvas-arrow/canvas-arrow.js"></script>

...
<script>
var context = canvas.getContext("2d");
context.beginPath();
context.arrow(0, 0, 200, 100, [0, 5, -20, 5, -20, 15]);
context.fill();
</script>


https://frogcat.github.io/canvas-arrow/canvas-arrow.js は CanvasRenderingContext2D の prototype に arrow メソッドを追加します。

arrow メソッドの APIはこんなかんじです。

context.arrow(startX,startY,endX,endY,controlPoints);



  • startX : 始点の X 座標


  • startY : 始点の Y 座標


  • endX : 終点の X 座標


  • endY : 終点の Y 座標


  • controlPoints : 矢印形状を指定するための制御点の配列

controlPoints には [x1,y1,x2,y2,x3,y3...] といったように制御点の X,Y 座標値が格納されます。

座標値の意味は下図のとおり。

image


  • x 座標が正の場合には start からの相対座標、負の場合には end からの相対座標と見なす

  • 終点の真上・真下を指定したい場合には -Number.MIN_VALUE などのとても小さな負の値を指定します


解説

結構短いコードです。


canvas-arrow.js

(function(target) {

if (!target || !target.prototype)
return;
target.prototype.arrow = function(startX, startY, endX, endY, controlPoints) {
var dx = endX - startX;
var dy = endY - startY;
var len = Math.sqrt(dx * dx + dy * dy);
var sin = dy / len;
var cos = dx / len;
var a = [];
a.push(0, 0);
for (var i = 0; i < controlPoints.length; i += 2) {
var x = controlPoints[i];
var y = controlPoints[i + 1];
a.push(x < 0 ? len + x : x, y);
}
a.push(len, 0);
for (var i = controlPoints.length; i > 0; i -= 2) {
var x = controlPoints[i - 2];
var y = controlPoints[i - 1];
a.push(x < 0 ? len + x : x, -y);
}
a.push(0, 0);
for (var i = 0; i < a.length; i += 2) {
var x = a[i] * cos - a[i + 1] * sin + startX;
var y = a[i] * sin + a[i + 1] * cos + startY;
if (i === 0) this.moveTo(x, y);
else this.lineTo(x, y);
}
};
})(CanvasRenderingContext2D);


  1. 一旦制御点座標系で矢印のポリゴンを作る

  2. そのポリゴンをアフィン変換して始点・終点座標系に投影する

  3. 結果を moveTo/lineTo で描画


まとめ

Canvas arrow で検索すると http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag こんなかんじでソリューションはあるのですが、矢印の形を変えるたびに座標変換やら三角関数やらを考えるのは精神衛生上よくありません。大きなライブラリに依存するのもちょっと、という場合に役に立つかもしれません。