Panda Noir

JavaScript の限界を究めるブログです。

Canvasをめちゃくちゃ使いやすくするライブラリ「UnitaryJS」作った

(この記事はQiitaで僕が書いたものを移行した記事です。記事中のコメントはQiitaの該当記事を参照ください)

ずっと思ってたんです。

「ctx.beginPath(); ctx.moveTo(x, y);」とか書くのは私の性に合わないな、と。

こんな無機質なプログラムは書きたくないな、と。

宣言的にCanvasを扱えるようなライブラリを開発せねば、と。

GitHub: UnitaryJS

logo.png

UnitaryJSとは

私たちは根本が間違えていたんだと思います。オブジェクトがあり、それをCanvasに描画する。これが自然な方法ですよね? ctx.beginPath(); とかいう “おまじない” をガリガリ書く のはプログラマの仕事ではありません。

UnitaryJSでは、下のような簡単で直感的なプログラムで三角形を書くことができます。三角形を宣言し、canvasにオブジェクトを追加して描画しているだけです。

// sample.js
const canvas = new Canvas('canvas');

const A = new Unitary.Point(50, 50),
    B = new Unitary.Point(30, 20),
    C = new Unitary.Point(70, 20),
    ABC = new Unitary.Triangle(A, B, C);

canvas.add(ABC);
canvas.draw();
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>SAMPLE</title>
    <script src="./unitary.js"></script> <!-- ライブラリ本体その1 -->
    <script src="./canvas.js"></script> <!-- ライブラリ本体その2 -->
    <script src="./sample.js"></script>
  </head>
  <body>
    <canvas id="canvas" width="200" height="200"></canvas>
  </body>
</html>

実行結果↓
triangle.png

(jsfiddleで上の実際のコードを触れます。実際のコード

サンプル集

Demo

API

UnitaryJSには以下のクラスがあります。

(以下のコードはUnitaryを省略しています。実際にはPointならUnitary.Point、LineならUnitary.Lineとなっているので注意してください)。

以下の情報は0.0.1バージョンのものなので詳しくはwikiを見てください。

Point

点です。 コンストラクタは2種類あります。

const A1 = new Point(0, 0);
const A2 = new Point.fromVector(new Vector(0, 0));

生成されるインスタンスはどちらも同じです。

Vector

ベクトルです。これはcanvas上に描画できません。

  • .add(n) ベクトルの加算です。
  • .minus(n) ベクトルの減算です。
  • .multiple(k) ベクトルの実数倍です。内積は下の.product()です。
  • .product(v) ベクトルの内積です。
  • .abs() ベクトルの絶対値を返します。

コンストラクタは2種類あります。

const vectorA = new Vector(10, 10);
const vectorOA = new Vector.from(new Point(10, 10), new Point(0, 0));

Line

直線です。線分はSegmentです。

const A = new Point(10,20);
const B = new Point(30,40);
const AB = new Line(A, B);

Segment

線分です。直線ではありません。

  • .has(P) 線分上に点Pがあるか返します。
const A = new Point(10,20);
const B = new Point(30,40);
const segmentAB = new Segment(A, B);

Circle

正円です。楕円は検討中です。

const O = new Point(30,30);
const C = new Circle(O, 30);

Polygon

多角形です。コンストラクタに渡した引数を頂点とする多角形を作ります。五芒星(一般に言う星形)もこのPolygonの一種です。だから頂点を結んだとき辺が交差してもok、としています。

const ABCDE = new Polygon(
    new Point(Math.cos(2 * Math.PI * (1/5) + Math.PI / 2) * 30 + 30, Math.sin(2 * Math.PI * (1/5) + Math.PI / 2) * 30 + 30),
    new Point(Math.cos(2 * Math.PI * (2/5) + Math.PI / 2) * 30 + 30, Math.sin(2 * Math.PI * (2/5) + Math.PI / 2) * 30 + 30),
    new Point(Math.cos(2 * Math.PI * (3/5) + Math.PI / 2) * 30 + 30, Math.sin(2 * Math.PI * (3/5) + Math.PI / 2) * 30 + 30),
    new Point(Math.cos(2 * Math.PI * (4/5) + Math.PI / 2) * 30 + 30, Math.sin(2 * Math.PI * (4/5) + Math.PI / 2) * 30 + 30),
    new Point(Math.cos(2 * Math.PI * (5/5) + Math.PI / 2) * 30 + 30, Math.sin(2 * Math.PI * (5/5) + Math.PI / 2) * 30 + 30)
)

Quadrilateral

四角形です。4頂点すべての座標をコンストラクタに渡してください。Polygonは辺同士の交差もokでしたが、砂時計型を四角形と呼ぶのはふさわしくないので砂時計型のときはエラーを出します。

const ABCD = new Quadrilateral(
    new Point(30,30),
    new Point(90,90),
    new Point(90,40),
    new Point(60,20)
);

Triangle

三角形です。3頂点の座標をコンストラクタに渡してください。3点で三角形を構築できないときはエラーを出します。

const ABCD = new Quadrilateral(
    new Point(30,30),
    new Point(90,90),
    new Point(90,40)
);
  • .getCircumcircle() 外接円オブジェクトを返します。
  • .getIncircle() 内接円オブジェクトを返します。
  • .getArea() 面積を返します。

Rect

矩形です。これは上のQuadrilateralとは違い、矩形しかかけません。左上と右下の頂点をコンストラクタに渡してください。

const ABCD = new Rect(
    new Point(30,30),
    new Point(90,90)
);

Text

テキストです。

Textコンストラクタなるものがあるらしい(Safariに存在していました)ので、グローバルオブジェクトにする時には気をつけてください。

  • .strokeOutline() 輪郭を描画するようにします。このメソッドを呼び出さないかぎり輪郭は描画しません。
  • .setOutlineColor(color) 輪郭の色を指定します。値はstrokeStyleで使える値としてください。
  • .setFillColor(color) 塗りつぶす色を指定します。値はfillStyleで使える値としてください。
  • .setBaseline() ベースラインを指定します。textBaselineで使える値としてください。
const circleC = new Circle(new Point(30, 30), 30),
const text = new Unitary.Text('O', circleC.Origin)); // Textコンストラクタをグローバルに置けないため

Image

画像です。

これもText同様Imageコンストラクタがグローバルに存在しているので間違えてグローバルのImageオブジェクトを上書きしないでください。canvas.jsの内部処理で使っているので正常に動かなくなる恐れがあります。

  • .trim(startPoint, sw, sh [, dw, dh]) トリム開始位置をstartPoint(Pointクラスのインスタンス)、トリムしたい幅、高さをsw、shに、トリム後縮小したい場合はdw、dhに幅と高さを指定してください。dw、dhを省略した場合sw、shと同じ値となります。
  • .resize(dw, dh) リサイズ後の幅、高さをdw、dhに渡してください。

Graph

グラフです。コンストラクタに1変数関数とスケールを渡すとグラフを書きます。

スケールというのは「1px/グラフ上の1が含むピクセル数」です。 Sampleで書いたグラフでは10pxごとに1となるようにグラフを描画しています。 単位がないので伝えづらいですね…

バージョン0.0.3から仕様変更しました。スケールがいくつのピクセルがグラフ上の1を構成しているかを示す値になりました。こうすることで「倍率を10倍上げる(つまり拡大)」という操作がそのままスケール値を10倍するだけでよくなりました。また、グラフ上に直線を引いたり点を打ったりする時もわかりやすくなります。

new Line(new Point(1 * scale, 1 * scale), new Point(3 * scale, 3 * scale)); // y = x という直線
new Point(1 * scale, 1 * scale); // グラフ上の(1, 1)の点

*.setRange(start, end) グラフの定義域を定義する関数です。指定しなかった場合はcanvasの端から端まで描画します。

Canvas

Canvasコンストラクタはcanvas.jsにあります。

new Canvas(id)

新しいCanvasを生成します。idには使いたいcanvas要素のidを渡してください。

Canvas.prototype.add(object)

描画したいobjectをCanvasに追加します。これだけでは描画されないので注意してください。

Canvas.prototype.draw()

canvas要素上に Canvas.prototype.add() で追加したオブジェクトを描画します。

Canvas.prototype.toDataURL()

canvas要素をdataURLに変換します。使い方はtoDataURLメソッドを参照してください。

modeプロパティ

通常はcanvas要素の左下を基点としたgraphモードで描画を行います。しかし、グラフ以外を描く時(画像を描く時など)左下基点では不便です。

そんな時には生成したCanvasインスタンスのmodeプロパティを "normal" に書き換えてください。modeプロパティが "normal" の時はcanvas要素の左上が基点、つまり通常のcanvasと同じ動作となります。