初心者の入門向け&私的忘備録です
記事作成&markdown形式は初めてなのでお見苦しい所、分かり辛い箇所等ありますが、お許しください。
まず最初に、あみだの縦棒の本数(x)を5、縦の長さ(y)を15としてみます。
tree = 5;
treeHeight = 15;
ブラウザをリロードするたびにあみだのルートが変わるランダム要素を付け加えるために、Math.randomを採用します。
Math.floor(Math.random() * 2);
これで、0の場合縦棒のみ。1の場合縦棒+横棒が描画されるようになります。
準備が整いました。描画していきます
流れとしてはまずは横一列に5本分描画してから、改行して次の列も描画するという流れを15回繰り返します。
コードにすると下記のような感じですね。x軸の間隔は50,y軸の間隔は30。
//getContext等canvas使う際の決り文句は省略
const tree = 5;
const treeHeight = 15;
function random(){
return Math.floor(Math.random() * 2);
}
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.strokeStyle = 'black';
for (let y = 1; y <= treeHeight; y++){
for (let x = 1; x <= tree; x++){
if (x === tree || random() === 0 || y === treeHeight){
ctx.beginPath();
ctx.moveTo( 50 * x ,y * 30 );
ctx.lineTo( 50 * x ,((y + 1) * 30));
ctx.stroke();
} else {
ctx.beginPath();
ctx.moveTo( 50 * x, y * 30 );
ctx.lineTo( 50 * x,((y + 1) * 30));
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.moveTo( 50 * (x + 1), y * 30 );
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.stroke();
x++;
}
}
}
1列ごとに、Math.randomで判定して0なら縦棒のみ、1なら縦棒&横棒+隣の縦棒("|_|"といった感じ)と、描画していきます。それと同時にあみだの右端、底辺は横棒を回避するように。
これで簡単なランダムなあみだの完成です。
下の2行を使ってmath.Randomで1になったときのx,yの座標を記録していきます。
redLine = [ ];(この配列に座標を格納します)
redPoint;
先程のあみだのコードにredLineの座標を収集するコードを追加していきます。
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.strokeStyle = 'black';
const redLine = [];
let redPoint;
for (let y = 1; y <= treeHeight; y++){
redPoint = 0;
redLine[y - 1] = [redPoint]; //ここで2重配列を作る
for (let x = 1; x <= tree; x++){
if (x === tree || random() === 0 || y === treeHeight){
redLine[y - 1][redPoint] = [0,0];
redPoint++;
ctx.beginPath();
ctx.moveTo( 50 * x ,y * 30 );
ctx.lineTo( 50 * x ,((y + 1) * 30));
ctx.stroke();
} else {
for (let t = 0; t < 2; t++){ //同じ座標を同じ列に2つ置く(右回り用と左回り用)
redLine[y - 1][redPoint]= [x, x + 1];
redPoint++;
}
ctx.beginPath();
ctx.moveTo( 50 * x, y * 30 );
ctx.lineTo( 50 * x,((y + 1) * 30));
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.moveTo( 50 * (x + 1), y * 30 );
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.stroke();
x++;
}
}
}
という感じに。[y座標[横棒開始のx座標,横棒終端のx座標]]です。
全分岐点の横棒の有無、座標が格納されたので後はこれを使って先程のあみだのコードの直下に追加します。
const undo = ctx.getImageData(0,0,canvas.width,canvas.height); //赤い線のない画像を保存しておく
ctx.strokeStyle = '#e65353';
ctx.lineWidth = 3;
for (let x = 1; x <= tree; x++){
input = document.createElement('input');
input.type = "submit";
input.value = "たどる"
input.addEventListener('click', () =>{
ctx.putImageData(undo,0,0); //保存掛けた画像を置いてあみだの赤い線を上書きする
r = x; //x座標を一時保存
for(let y = 1; y <= treeHeight; y++){
ctx.moveTo( 50 * x, y * 30 );
if(redLine[y - 1][x - 1][0] === x){ //配列の0番目の数値が現在地点だったら右に移動
ctx.beginPath();
ctx.moveTo( 50 * x, y * 30 );
ctx.lineTo( 50 * x,((y + 1) * 30));
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.stroke();
x++;
} else if(redLine[y - 1][x - 1][1] === x){ //配列の1番目の数値が現在地点だったら左に移動
ctx.beginPath();
ctx.moveTo( 50 * x, y * 30 );
ctx.lineTo( 50 * x,((y + 1) * 30));
ctx.lineTo( 50 * (x - 1),((y + 1) * 30));
ctx.stroke();
x--;
} else{
ctx.beginPath();
ctx.moveTo( 50 * x ,y * 30 );
ctx.lineTo( 50 * x ,((y + 1) * 30));
ctx.stroke();
}
}
x = r; //あっちこっちに移動したxの座標を初期化する
});
document.body.appendChild(input);
}
という具合にです、無事にあみだを辿るコードを追加することが出来ました。
最後にコードの全体図を載せておきます
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>あみだくじ試作</title>
</head>
<body>
<canvas width="330" height="530" id="canvas" style="background: #ddd;">
canvas not supported.....
</canvas>
<script type="text/javascript" src="main.js"></script>
</body>
</html>
{
'use strict'
function makeAmida() {
const canvas = document.getElementById('canvas');
if (!canvas.getContext || !canvas){
return;
}
const ctx = canvas.getContext('2d');
const tree = 5;
const treeHeight = 15;
function random() {
return Math.floor(Math.random() * 2);
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'black';
const redLine = [];//あみだを辿るときに使う座標を格納するコンテナ
let redPoint;
for (let y = 1; y <= treeHeight; y++){
redPoint = 0;
redLine[y - 1] = [redPoint];//ここで2重配列を作る
for (let x = 1; x <= tree; x++){
if (x === tree || random() === 0 || y === treeHeight){
redLine[y - 1][redPoint] = [0, 0];
redPoint++;
ctx.beginPath();
ctx.moveTo( 50 * x ,y * 30 );
ctx.lineTo( 50 * x ,((y + 1) * 30));
ctx.stroke();
} else {
for (let t = 0; t < 2; t++){ //同じ座標を同じ列に2つ置く(右回り用と左回り用)
redLine[y - 1][redPoint]= [x, x + 1];
redPoint++;
}
ctx.beginPath();
ctx.moveTo( 50 * x, y * 30 );
ctx.lineTo( 50 * x,((y + 1) * 30));
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.moveTo( 50 * (x + 1), y * 30 );
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.stroke();
x++;
}
}
}
const undo = ctx.getImageData(0, 0, canvas.width, canvas.height); //赤い線のない画像を保存しておく
ctx.strokeStyle = '#e65353';
ctx.lineWidth = 3;
for (let x = 1; x <= tree; x++){
input = document.createElement('input');
input.type = "submit";
input.value = "たどる"
input.addEventListener('click', () =>{
ctx.putImageData(undo, 0, 0); //保存掛けた画像を置いてあみだの赤い線を上書きする
r = x; //x座標を一時保存
for(let y = 1; y <= treeHeight; y++){
ctx.moveTo( 50 * x, y * 30 );
if(redLine[y - 1][x - 1][0] === x){ //redLine配列の情報:[y座標[横棒始点のx, 横棒終点のx]]
ctx.beginPath();
ctx.moveTo( 50 * x, y * 30 );
ctx.lineTo( 50 * x,((y + 1) * 30));
ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
ctx.stroke();
x++;
} else if(redLine[y - 1][x - 1][1] === x){
ctx.beginPath();
ctx.moveTo( 50 * x, y * 30 );
ctx.lineTo( 50 * x,((y + 1) * 30));
ctx.lineTo( 50 * (x - 1),((y + 1) * 30));
ctx.stroke();
x--;
} else{
ctx.beginPath();
ctx.moveTo( 50 * x ,y * 30 );
ctx.lineTo( 50 * x ,((y + 1) * 30));
ctx.stroke();
}
}
x = r; //あっちこっちに移動したxの座標を初期化する
});
document.body.appendChild(input);
}
}
makeAmida();
}
初心者でも意外と簡単に作れるので、CSSで装飾したり、あみだの本数を選択式にして自在に長さ変られるようにしたり、変形させたりさまざまなあみだくじを作ってみてください。暇つぶしやプログラミングの練習、夏休みの自由研究等にどうぞ。
いまいちピンとこない箇所があれば質問大歓迎です(分かる範囲でお答え致します)
ちなみに先日、上記の2つを応用してこんな物を作りました
あみだくじ
(github: https://github.com/usukonsome/makeamida)
コードレビューお待ちしております(小声)