ロゴシュミグラム

JavascriptでCanvasに幾何学模様を描こう!その1

このプログラムについて

Javascriptでcanvas要素に幾何学模様を描きます。模様を描くためには三角関数を使った計算をします。三角関数の解説もしていますが、わからなくてもプログラムは作ることができます。

必要なプログラミング知識

HTMLとCSSの基礎
Javascriptの基礎(変数や関数の使い方、DOMの操作方法など)

各部位の名前

次のように各部位の名前付けをします。英語名は変数名を表しています。

幾何学模様のパーツの呼び方

時計の針のようにクルクル回る線を回転針(needle)、その関節部分を節(node)、節の軌道を描いた線を軌道線(trajectory)とよぶことにします。

ファイルの作成

次のようにgeopattern-genフォルダを作成し、その中にHTMLファイル、CSSファイル、JSファイルを作成します。


geopattern-gen
┣━ index.html
┣━ styles.css
┣━ settings.js
┣━ canvas.js
┣━ functions.js
┗━ main.js

settings.jsには幾何学模様の設定、canvas.jsにはcanvasを操作するための関数、functions.jsにはそれ以外の関数を作成していきます。

HTMLの作成

index.htmlに次のように書き込みます。

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>幾何学模様</title>
<link rel="stylesheet" type="text/css" href="styles.css">
<script defer src="settings.js"></script>
<script defer src="canvas.js"></script>
<script defer src="functions.js"></script>
<script defer src="main.js"></script>
</head>
<body>
<div id="canvas-wrapper">
<canvas id="main-canvas"></canvas>
<canvas id="needle-canvas"></canvas>
</div>
<div id="button-area">
<button id="draw-button">描く</button>
<button id="start-button">自動描画</button>
<button id="stop-button">中断</button>
<button id="reset-button">リセット</button>
</div>
</body>
</html>

head要素内のscriptは main.js が最後になるようにしてください。また、scriptに defer を付けないとHTML解析前にJavascriptを実行してしまうためエラーとなります。

canvas要素は2つ用意してあります。

id="main-canvas" のほうには軌道線を描きます。回転針が回るごとに軌道線を付け足していきます。

id="needle-canvas" のほうは回転針を描きます。回転針が回るごとにcanvasをすべてけして、そのあと新しい回転針を描きます。こうすることで回転針がクルクル回っているようにみえます。

CSSの作成

次にstyles.cssに次のように書き込みます。

styles.css

body {
margin: 0
}
canvas {
display: block;
width: 100%
}
#canvas-wrapper {
position: relative;
max-width: 600px;
border: 5px ridge darkorange;
border-radius: 4px;
overflow: hidden;
margin: auto;
}
#needle-canvas {
position: absolute;
top: 0;
left: 0;
}
#button-area {
display: flex;
justify-content: center;
margin-top: 8px;
gap: 8px;
}

2つのcanvasを重ね合わせるため、 needle-canvas のほう position:absolute にしています。これを無くすとそれぞれのcanvasがどのようなものを描いているのか確認することができます。

設定ファイルの作成

settings.jsに次のような設定を書いていきます。

変数名説明
canvasSizecanvas要素の大きさ
drawInterval線を描く時間の間隔(単位はミリ秒)。大きくなるほどゆっくりと描くようになる。
bgColorcanvasの背景色
lineDataList線の設定の配列

線の設定はオブジェクトとして書きます。これには次のようなプロパティがあります。

変数名説明
radius線の長さ。radiusとは半径の意味
angle線を回転させる角度。マイナスの値にすると時計回りに回転する
drawTrajectory軌道線を描くか否か
color軌道線の色

それぞれの意味は次のようになります。

幾何学模様のパーツの呼び方

settings.jsに次のように書き込みます。

settings.js

// canvas要素の大きさ
const canvasSize = 600;
// 自動描画するときの時間の間隔(単位はミリ秒)
const drawInterval = 20;
// canvasの背景色
// const bgColor = '#fff';
const bgColor = '#F6AD55';
// 線の設定の配列
// const lineDataList = [
// {
// radius: 100,
// angle: -30,
// drawTrajectory: true,
// color: 'red'
// },
// {
// radius: 120,
// angle: -95,
// drawTrajectory: true,
// color: 'blue'
// },
// // {
// // radius: 40,
// // angle: -20,
// // drawTrajectory: true,
// // color: 'green'
// // },
// ];
const lineDataList = [
{
radius: 100,
angle: -57.3,
drawTrajectory: false,
color: '#000000'
},
{
radius: 82,
angle: 16.2,
drawTrajectory: false,
color: '#000000'
},
{
radius: 78,
angle: -155.7,
drawTrajectory: true,
color: '#000000'
},
];

描画コンテキストの初期設定

描画コンテキストとは、canvasに絵を描くための画用紙や絵の具が入っている道具箱のようなものです。これの初期設定をおこないます。

canvasの座標

canvasにおける座標は、左上を原点として下に行くほどy値が大きくなります。たとえば、widthとheightが600pxのcanvasの場合は次のようになります。

canvasの座標

幾何学模様を描くには三角関数を使います。その場合、数学でよく使われる、中央が原点で上に行くほどy値が大きくなる次のような座標のほうがプログラムを書きやすくなります。

canvasの変換後の座標

このようにcanvasの座標を変換していきます。

描画コンテキストを設定して取得する関数

canvas.jsに次のように書き込みます

canvas.js

/**
* 初期設定した描画コンテキストを取得する関数
* @param canvasId canvas要素のID
* @return 描画コンテキスト
*/
const initCanvasCtx = (canvasId) => {
// canvas要素を取得して大きさを設定する
const canvas = document.getElementById(canvasId);
canvas.width = canvasSize;
canvas.height = canvasSize;
// 描画コンテキストを取得する
const ctx = canvas.getContext("2d");
// 原点(0, 0)をcanvasの中央に移動する
ctx.translate(canvasSize / 2, canvasSize / 2);
// y軸を反転する
ctx.scale(1, -1);
return ctx;
};

座標位置の表し方

このプログラムでは座標位置をオブジェクトで表すことにします。

たとえば、x座標が200、y座標が300の位置を示すには次のようなオブジェクトを作ります。

座標位置を表すオブジェクト

const samplePos = { x: 200, y: 300 };

変数名に「pos」と付いているものはこのような位置を表すオブジェクトが入っていると思ってください。

線を描く関数の作成

canvas.jsに次のように書き込みます。

canvas.js

/**
* canvasに線を描く関数
* @param startPos 線を引き始める座標位置
* @param endPos 線を引き終える座標位置
* @param color 線の色
* @param width 線の太さ
* @param ctx 描画コンテキスト
*/
const drawLine = (startPos, endPos, color, width, ctx) => {
// 線の色と太さを設定
ctx.strokeStyle = color;
ctx.lineWidth = width;
// 線を引き始める合図
ctx.beginPath();
// 線を引き始める座標位置を決める
ctx.moveTo(startPos.x, startPos.y);
// 線を引き終える座標位置を決める
ctx.lineTo(endPos.x, endPos.y);
// 線を描く
ctx.stroke();
};

点を描く関数の作成

canvas.jsに次のように書き込みます。関数内で使われている radian360 の意味はあとで説明します。

canvas.js

/**
* canvasに点を描く関数
* @param pos 点を描く座標位置
* @param color 点の色
* @param radius 点の半径
* @param ctx 描画コンテキスト
*/
const drawPoint = (pos, color, radius, ctx) => {
// 点を塗りつぶす色の設定
ctx.fillStyle = color;
ctx.beginPath();
// 引数にはx座標、y座標、半径、開始角度、終了角度を渡す
ctx.arc(pos.x, pos.y, radius, 0, radian360);
// 点を描く
ctx.fill();
};

軌道線を描く関数の作成

canvas.jsに次のように書き込みます。

canvas.js

/**
* canvasに軌道線を描く関数
* @param prevNodePosList 回転前のnodePosList
* @param nextNodePosList 回転後のnodePosList
* @param LineDataList 線情報の配列
* @param ctx 描画コンテキスト
*/
const drawTrajectories = (prevNodePosList, nextNodePosList, LineDataList, ctx) => {
LineDataList.forEach((lineData, i) => {
if (lineData.drawTrajectory) {
// nodePosList[0]は{ x: 0, y: 0 }であるため、軌道線を描く必要なない
drawLine(prevNodePosList[i + 1], nextNodePosList[i + 1], lineData.color, 1, ctx);
};
});
};

回転針を描く関数の作成

canvas.jsに次のように書き込みます。

canvas.js

/**
* canvasに回転針を描く関数
* @param nodePosList 節の座標位置の配列
* @param LineDataList 線設定の配列
* @param ctx 描画コンテキスト
*/
const drawNeedle = (nodePosList, lineDataList, ctx) => {
// 線を描く
lineDataList.forEach((lineData, i) => {
drawLine(nodePosList[i], nodePosList[i + 1], 'gray', 3, ctx);
});
// canvas中心に点を描く
// nodePosList[0]には{ x:0, y:0 }が入っている
drawPoint(nodePosList[0], 'gray', 3, ctx);
// 節となる点を描く
lineDataList.forEach((lineData, i) => {
if (lineData.drawTrajectory) {
// 軌道線を描く場合は、点をその色に染める、半径をやや大きくする
drawPoint(nodePosList[i + 1], lineData.color, 5, ctx);
} else {
drawPoint(nodePosList[i + 1], 'gray', 3, ctx);
}
});
};

canvasに描いたものをすべて消す関数の作成

canvas.jsに次のように書き込みます。

canvas.js

/**
* canvasに描いたものをすべて消す関数
* @param ctx 描画コンテキスト
*/
const clearCanvas = (ctx) => {
// 引数には消す範囲のx座標、y座標、横幅、縦幅を渡す
ctx.clearRect(-canvasSize / 2, -canvasSize / 2, canvasSize, canvasSize);
};

canvasを指定色で塗りつぶす関数の作成

canvas.jsに次のように書き込みます。

canvas.js

/**
* canvasを指定色で塗りつぶす関数
* @param color 塗りつぶす色
* @param ctx 描画コンテキスト
*/
const fillBgCanvas = (color, ctx) => {
ctx.fillStyle = color;
// 引数には塗りつぶす範囲のx座標、y座標、横幅、縦幅を渡す
ctx.fillRect(-canvasSize / 2, -canvasSize / 2, canvasSize, canvasSize);
};