rm /blog

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【HTML5】canvasを使ってチープな「雷」を描いてみた

HTML5の「canvas」の練習用に、チープな「雷」を描画するプログラムを作ってみた。
こんなやつ↓

20180831_blog_canvas_image.png


 

 


「こんなことやればパッと見『雷』っぽくなるんじゃねーの?」と思ったのが以下のポイントである。

  1. 黒い背景に白い小さな点を上から下に向かって順次描画していく
  2. 描画する度、点の描画位置を現在位置から±いくつか変化させて、ランダムウォークっぽくする
  3. これを複数回(3~5回くらい)繰り返す


こんだけ。
こんくらいならできそうかもなってやってみた。
ソースは以下。

<!DOCTYPE HTML>
<html>
	<head>
		<meta charset="UTF-8">
		<style type="text/css">
		#cvs-canvas {
			background-color:#000000;
		}
		</style>
		<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
		<script type="text/javascript">
		var intArcRadius = 1; // 雷の1点1点の半径
		var intThunderCount = 5; // 雷の本数
		
		$(document).ready(function(){
			
			var objCanvas = document.getElementById("cvs-canvas");
			var objCanvasContext = objCanvas.getContext('2d');
			
			var intCanvasWidth = objCanvas.width;
			var intCanvasHeight = objCanvas.height;
			
			var intWidthLeftLimit = 10;                   // 左側の限界(これ以上は左側にいかないようにする)
			var intWidthRightLimit = intCanvasWidth - 10; // 右側の限界(これ以上は右側にいかないようにする)
		
			// 雷の描画開始
			$("#btn-thunder").click(function(){
				//描画済みの雷をクリア
				objCanvasContext.beginPath();
				objCanvasContext.clearRect(0,0,intCanvasWidth,intCanvasHeight);
				// 雷の本数分描画
				for(intRound = 0; intRound < intThunderCount; intRound++) {
					var intDrawX = getRandom(intWidthLeftLimit,intWidthRightLimit);
					// キャンバスの一番上から順に点を描画していく
					for(intDrawY = 0; intDrawY <= intCanvasHeight; intDrawY = intDrawY + intArcRadius) {
						// 円弧の範囲(どこまで描くか)
						var dblRound = getRandom(0,Math.PI * 2);
					
						objCanvasContext.beginPath();
						objCanvasContext.strokeStyle  = 'rgba(255, 255, 255)'; // とりあえず白色を指定
						objCanvasContext.fillStyle  = 'rgba(255, 255, 255)'; // とりあえず白色を指定
						objCanvasContext.arc(intDrawX , intDrawY , intArcRadius , 0 , dblRound , true);
						objCanvasContext.stroke();
						objCanvasContext.fill();
					
						// 次に描画する点の位置を決める(現在位置±点の半径のどこか)
						var intNextDrawXPointFrom = intDrawX - intArcRadius;
						var intNextDrawXPointTo   = intDrawX + intArcRadius;
						if (intNextDrawXPointFrom < intWidthLeftLimit) {
							intNextDrawXPointFrom = intWidthLeftLimit;
						}
						if (intNextDrawXPointTo > intWidthRightLimit) {
							intNextDrawXPointTo = intWidthRightLimit - 1;
						}
						intDrawX = getRandom(intNextDrawXPointFrom , intNextDrawXPointTo);
					
					}
				}
			});
			
		});
		function getRandom(intFrom,intTo) {
			return Math.floor(Math.random() * (intTo - intFrom + 1) +intFrom);
		}
		</script>
		<title>thunder</title>
	</head>
	<body>
		<canvas id="cvs-canvas" height="300" width="100">
		</canvas>
		<p></p>
		<input type="button" value="thunder draw" id="btn-thunder">
		<p></p>
	</body>
</html>


「thunder draw」ボタンを押下する度、真っ黒背景のキャンバスにランダムに5本のチープな「雷」が描画される。
ボタン押下の度にキャンバスをクリアして再描画するので毎回現れる形は変わる。

「雷の本数」が最初のループになっていて、それのネストループに「描画」処理がある。
言い換えれば「描画」処理が「雷の本数」分繰り返される形。

「描画」処理では、キャンバスのheightを取得し、0(一番上)から順にheightになるまで点の描画を繰り返す。
点の半径は1ピクセルなので、1点1点は非常に小さく、結果を見ても個々の「点」の存在は認知できない(はずである)
これが順次、上から下に連なることで「線」っぽくなり、そちらの印象が強くなる(という想定なのだ)
また、個々の点の描画位置を現在位置から少しだけ、ただしランダムに変更させることで、結果が微妙にギザギザになり、これが結果的に「雷」っぽさの表現になる(と思っている)

「ランダム」にしているのは

  • 描画する点のX座標の位置(前回描画位置±点の半径)
  • 描画する点の円弧の範囲(どこまで円弧を描くか)

であるが、2点目はオマケである。
(不完全な「円」が所々に現れることでより「ギザギザ」っぽさが増すかな、という程度で組み入れただけ)

赤太字にしている箇所がcanvasオブジェクトを使った描画の主処理にあたる。
今回知ったのはcanvas#beginPath」ですな。
「一度現在パスをリセットさせないと適切に描画できない」というのはいい勉強になった。
try-catchみたいなトランザクション処理の記述っぽさを感じた。

なお、今回、点の描画にcanvas#arc」という円を描くためのメソッドを使ったが、目的は「点を連ねて線にする」なので、別に円に拘る必要はない。
個々の点は細かく小さければなんでもいいので、「すごく小さい正方形」とかでも、同様にそれっぽくなるだろう(試してないが)。

キャンバスのサイズ、点の半径、円弧の範囲、円弧を描く方向、雷の本数、、、etc
こんな程度の処理においても、実行するだけで複数の「パラメータ」があり、この辺を少し操作するだけでいろいろな「表現」が可能になるだろう。
実際の雷の原理はよく知らないが、パッと見それっぽいのが再現出来たのと、そもそもの目的であるcanvasの勉強にはなったので、大体満足したってことで、今回はこれでヨシとする。
最後に、この処理で自作した雷を何点か挙げておく。

  • 20180831_blog_canvas_image_pattern01.png
  • 20180831_blog_canvas_image_pattern02.png
  • 20180831_blog_canvas_image_pattern03.png
  • 20180831_blog_canvas_image_pattern04.png