プログラミングとイラストレーション » HTML5のCanvasにJavaScriptでテオ・ヤンセンのビーストを再現する
プログラミングとイラストレーション > HTML5 > HTML5のCanvasにJavaScriptでテオ・ヤンセンのビーストを再現する

HTML5のCanvasにJavaScriptでテオ・ヤンセンのビーストを再現する

HTML5のCanvasにJavaScriptでテオ・ヤンセンのビーストを再現する

HTML5のCanvasにJavaScriptでテオ・ヤンセンのビーストを再現する : デモ

HTML5のCanvasにJavaScriptでテオ・ヤンセンのビーストを再現する : ZIPファイル(7kb)

テオ・ヤンセンのストランドビーストの各脚のリンク機構は、余弦定理を使って導きます。

余弦定理を使って、フォワードキネマティクスを実現するための図と数式

1.HTML

<!doctype html> <!-- 標準モードでWebページをレンダリング -->
<html lang="ja"> <!-- 言語指定付きのhtml要素 -->
	<head> <!-- ヘッダーの指定 HTMLページの生成に必要な情報を記述 -->
		<meta charset="UTF-8"> <!-- Web上の文字エンコーディングの指定 -->
		<title>HTML5のCanvasにJavaScriptでテオ・ヤンセンのビーストを再現する</title> <!-- ブラウザの上部ウィンドウのバーに表示されるタイトル -->
        <link href="css/base.css" rel="stylesheet" type="text/css"> <!-- cssファイル(デザインの仕様)の読み込み -->
		<script src="js/base.js"></script> <!-- JavaScriptファイル(制御の仕様)の読み込み -->
	</head>

    <body> <!-- HTMLページのメインコンテンツを示す要素 -->
    	<canvas id = "viewArea2" width = "400" height = "300"></canvas> <!-- 描画領域 -->
        <canvas id = "viewArea" width = "400" height = "300"></canvas> <!-- 描画領域 -->
    </body>
</html>

2.CSS

@charset "UTF-8";

body /* bodyのデザイン */
{
	margin:0; /* borderより外側の余白は0 */
	padding:0; /* borderより内側の余白は0 */
	background-color: #fff; /* 背景色は白 */ 
	-webkit-transform:translateZ(0px); /* GPUの機能を使う */
}

#viewArea, #viewArea2 /* id名「viewArea」が付いたCanvas要素(タグ)のデザイン */
{
	position: absolute; /* 配置方法は絶対値 */
	top: 10px; /* Webのページの上から10ピクセルの位置 */
	left: 10px; /* Webのページの左から10ピクセルの位置 */
	width: 400px; /* 幅は275ピクセル */
	height: 300px; /* 高さは275ピクセル */
	border: 1px solid #000; /* 線は1ピクセルの太さで、実線、色は黒。上下左右に引く */
}

3.JavaScript

base.js

window.addEventListener("load", init, false);

function init()
{
	//変数の定義
	var offsetX = 0,
	offsetY = 0,
	centerX = 0,
	centerY = 0,
	ctx = null,
	ctx2 = null,
	radius = 3,
	ratio = 0.4,

	y_oa = 67.5 * ratio, //70

	x_a = 0,
	y_a = y_oa,

	//left_side  start
	x_b = -162 * ratio, //-162
	y_b = 0 * ratio,
	x_c_init = -93 * ratio,
	y_c_init = -140 * ratio,
	x_e_init = -213 * ratio,
	y_e_init = 149 * ratio,
	
	x_f = 0,
	y_f = 0,
	x_g = 0,
	y_g = 0,
	x_h = 0,
	y_h = 0,

	x_g_init = -360 * ratio,
	y_g_init = 74 * ratio,
	x_f_init = -310 * ratio,
	y_f_init = -75 * ratio,
	x_h_init = -329 * ratio, //-329 -306
	y_h_init = 378 * ratio,  //378   333

	side_a = 0,
	side_b = 0,
	side_c = 0,
	side_c2 = 0,
	side_e = 0,
	side_a2 = 0,
	side_b2 = 0,
	side_b3 = 0,
	side_f = 0,
	side_e2 = 0,
	side_e3 = 0,
	side_g = 0,
	side_g2 = 0,
	side_h = 0,
	//left_side end

	//right_side  start
	_x_c_init = x_c_init,
	_y_c_init = y_c_init,
	_x_e_init = x_e_init,
	_y_e_init = y_e_init,
	_x_g_init = x_g_init,
	_y_g_init = y_g_init,
	_x_f_init = x_f_init,
	_y_f_init = y_f_init,
	_x_h_init = x_h_init,
	_y_h_init = y_h_init,

	_x_b = -x_b,
	_y_b = y_b,
	_x_c = 0,
	_y_c = 0,
	_x_e = 0,
	_y_e = 0,
	_x_f = 0,
	_y_f = 0,
	_x_g = 0,
	_y_g = 0,
	_x_h = 0,
	_y_h = 0,
	//right_side end

	theCanvas = null,
	theCanvas2 = null,
	rad = 90 * Math.PI/180;
	
	//視覚の処理を行うオブジェクトのグループ
	View =
	{
		//空のinit関数を作る
		init : function()
		{

		}
	}
	
	//Viewの中の関数(init)にプロトタイプの変数や関数を定義します
	//プロトタイプには、インスタンスで使う共有の関数や変数を設定します
	View.init.prototype.animation = function()
	{
		rad += 0.05;
		View.init.prototype.pointInit();
	}

	View.init.prototype.pointInit = function()
	{
		requestAnimationFrame(View.init.prototype.animation);
		x_a = Math.cos(rad) * y_oa;
		y_a = Math.sin(rad) * y_oa;

		ctx.clearRect(0, 0, offsetX, offsetY);

		ctx.fillStyle = "#000000";
		ctx.beginPath();
		//ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)
		ctx.arc(centerX, centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		
		ctx.beginPath();
		ctx.arc(x_a+centerX, y_a+centerY, radius, 0, Math.PI*2, false);
		ctx.fill();

		//頂点C ここから
		//辺AC
		var dx = x_a - x_b;
		var dy = y_a - y_b;
		var side_c = Math.sqrt(dx*dx+dy*dy);
		var D = Math.atan2(dy, dx);

		//余弦定理からラジアン角度を算出します。
		var B = Math.acos((side_c * side_c + side_a * side_a - side_b * side_b) / (2 * side_c * side_a));
		
		B -= D;

		var x_c = Math.cos(B) * side_a;
		var y_c = Math.sin(B) * side_a;

		ctx.beginPath();
		ctx.arc(x_c + x_b + centerX, -y_c + y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点C ここまで

		//頂点E ここから
		//辺AE
		side_e = side_c;
		
		//余弦定理からラジアン角度を算出します。
		B = Math.acos((side_a2 * side_a2 + side_e * side_e - side_b2 * side_b2) / (2 * side_a2 * side_e));
		
		B += D;

		var x_e = Math.cos(B) * side_a2;
		var y_e = Math.sin(B) * side_a2;

		ctx.beginPath();
		ctx.arc(x_e + x_b + centerX, y_e + y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点E ここまで


		//頂点F ここから
		//辺BF

		//余弦定理からラジアン角度を算出します。
		B = Math.acos((side_a * side_a + side_c2 * side_c2 - side_b3 * side_b3) / (2 * side_a * side_c2));
		
		dx = x_c;
		dy = y_c;
		D = Math.atan2(dy, dx);
		
		B += D;

		var x_f = Math.cos(B) * side_c2;
		var y_f = Math.sin(B) * side_c2;

		ctx.beginPath();
		ctx.arc(x_f + x_b + centerX, -y_f + y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点F ここまで

		
		//頂点G ここから
		//辺GE
		dx = x_f - x_e;
		dy = y_f - y_e;
		D = Math.atan2(dy, dx);

		var side_g  = Math.sqrt(dx*dx+dy*dy);

		//余弦定理からラジアン角度を導く
		B = Math.acos((side_f * side_f + side_g * side_g - side_e2 * side_e2) / (2 * side_f * side_g));
		
		B -= D;

		var x_g = Math.cos(B) * side_f;
		var y_g = Math.sin(B) * side_f;

		ctx.beginPath();
		ctx.arc(x_g + x_e + x_b + centerX, y_g + y_e + y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点G ここまで

		//辺EH
		//余弦定理からラジアン角度を算出します。
		B = Math.acos((side_h * side_h + side_g2 * side_g2 - side_e3 * side_e3) / (2 * side_h * side_g2));
		
		dx = (x_e+x_b+centerX) - (x_g+x_e+x_b+centerX);
		dy = (y_e+y_b+centerY) - (y_g+y_e+y_b+centerY);
		D = Math.atan2(dy, dx);

		B += D;

		var x_h = Math.cos(B) * side_g2;
		var y_h = Math.sin(B) * side_g2;

		ctx.beginPath();
		ctx.arc(x_h + x_e + x_b + centerX, y_h + y_e + y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点H ここまで
		
		//頂点B
		//ctx.fillStyle = "#000000";
		ctx.beginPath();
		ctx.arc(x_b+centerX, y_b+centerY, radius, 0, Math.PI*2, false);
		ctx.fill();



		//right_side start
		_x_c = -x_c,
		_y_c = y_c,
		_x_e = -x_e,
		_y_e = y_e,
		_x_f = -x_f,
		_y_f = y_f,
		_x_g = -x_g,
		_y_g = y_g,
		_x_h = -x_h,
		_y_h = y_h,

		//頂点_C ここから
		//辺AC
		dx = x_a - _x_b;
		dy = y_a - _y_b;
		
		_side_c = Math.sqrt(dx*dx+dy*dy);

		D = Math.atan2(dy, dx);

		//余弦定理からラジアン角度を算出します。
		B = Math.acos((_side_c * _side_c + side_a * side_a - side_b * side_b) / (2 * _side_c * side_a));
		
		B += D;
		
		var _x_c = Math.cos(B) * side_a;
		var _y_c = Math.sin(B) * side_a;

		//ctx.fillStyle = "#00fff0";
		ctx.beginPath();
		ctx.arc(_x_c + _x_b + centerX, _y_c + _y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点_C ここまで


		
		//頂点E ここから
		//辺AE
		_side_e = _side_c;

		dx = x_a - _x_b;
		dy = y_a - _y_b;

		D = Math.atan2(dy, dx);
		
		//余弦定理からラジアン角度を算出します。
		B = Math.acos((side_a2 * side_a2 + _side_e * _side_e - side_b2 * side_b2) / (2 * side_a2 * _side_e));
		
		B -= D;

		var _x_e = Math.cos(B) * side_a2;
		var _y_e = -Math.sin(B) * side_a2;

		//ctx.fillStyle = "#00fff0";
		ctx.beginPath();
		ctx.arc(_x_e + _x_b + centerX, _y_e + _y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点E ここまで

		
		//頂点F ここから
		//辺BF
		dx = _x_c// - _x_b;
		dy = _y_c// - _y_b;
		D = Math.atan2(dy, dx);

		//余弦定理からラジアン角度を算出します。
		B = Math.acos((side_a * side_a + side_c2 * side_c2 - side_b3 * side_b3) / (2 * side_a * side_c2));
		
		B += D;

		var _x_f = Math.cos(B) * side_c2;
		var _y_f = Math.sin(B) * side_c2;

		//ctx.fillStyle = "#0000ff";
		ctx.beginPath();
		ctx.arc(_x_f + _x_b + centerX, _y_f + _y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点F ここまで

		
		//頂点G ここから
		//辺GE
		dx = _x_f - _x_e;
		dy = _y_f - _y_e;
		D = Math.atan2(dy, dx);

		var _side_g  = Math.sqrt(dx*dx+dy*dy);

		//余弦定理からラジアン角度を導く
		B = Math.acos((side_f * side_f + _side_g * _side_g - side_e2 * side_e2) / (2 * side_f * _side_g));
		
		B += D;


		var _x_g = Math.cos(B) * side_f;
		var _y_g = Math.sin(B) * side_f;

		ctx.beginPath();
		ctx.arc(_x_g + _x_e + _x_b + centerX, _y_g + _y_e + _y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点G ここまで
		
		
		//頂点H ここから
		dx = (_x_e + _x_b) - (_x_g + _x_e + _x_b);
		dy = (_y_e + _y_b) - (_y_g + _y_e + _y_b);

		D = Math.atan2(dy, dx);

		//余弦定理からラジアン角度を算出します。
		B = Math.acos((side_h * side_h + side_g2 * side_g2 - side_e3 * side_e3) / (2 * side_h * side_g2));
		
		B -= D;

		var _x_h = Math.cos(B) * side_g2;
		var _y_h = -Math.sin(B) * side_g2;

		ctx.fillStyle = "#000000";
		ctx.beginPath();
		ctx.arc(_x_h + _x_e + _x_b + centerX, _y_h + _y_e + _y_b + centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//頂点H ここまで
		
		
		//頂点B
		ctx.fillStyle = "#000000";
		ctx.beginPath();
		ctx.arc(_x_b+centerX, _y_b+centerY, radius, 0, Math.PI*2, false);
		ctx.fill();
		//right_side end


		//線をつなげる
		ctx.strokeStyle = "#000000";
		ctx.lineWidth = 0;
		ctx.beginPath();
		ctx.moveTo(centerX, centerY);
		ctx.lineTo(x_a + centerX, y_a + centerY);
		ctx.lineTo(x_c + x_b + centerX, -y_c + y_b + centerY);
		ctx.moveTo(x_a + centerX, y_a + centerY);
		ctx.lineTo(x_e + x_b + centerX, y_e + y_b + centerY);
		ctx.moveTo(x_b + centerX, y_b + centerY);
		ctx.lineTo(x_e + x_b + centerX, y_e + y_b + centerY);
		ctx.moveTo(x_b + centerX, y_b + centerY);
		ctx.lineTo(x_c + x_b + centerX, -y_c + y_b + centerY);
		ctx.lineTo(x_f + x_b + centerX, -y_f + y_b + centerY);
		ctx.moveTo(x_b + centerX, y_b + centerY);
		ctx.lineTo(x_f + x_b + centerX, -y_f + y_b + centerY);
		ctx.lineTo(x_g + x_e + x_b + centerX, y_g + y_e + y_b + centerY);
		ctx.lineTo(x_e + x_b + centerX, y_e + y_b + centerY);
		ctx.lineTo(x_h + x_e + x_b + centerX, y_h + y_e + y_b + centerY);
		ctx.moveTo(x_g + x_e + x_b + centerX, y_g + y_e + y_b + centerY);
		ctx.lineTo(x_h + x_e + x_b + centerX, y_h + y_e + y_b + centerY);
		ctx.stroke();

		//歩行の軌跡を描く
		ctx2.lineWidth = 0;
		ctx2.fillStyle = "#cccccc";
		ctx2.beginPath();
		ctx2.arc(x_h + x_e + x_b + centerX, y_h + y_e + y_b + centerY, 0.5, 0, Math.PI*2, false);
		ctx2.fill();


		//left
		ctx.strokeStyle = "#000000";
		ctx.lineWidth = 0;
		ctx.beginPath();
		ctx.moveTo(centerX, centerY);
		ctx.lineTo(x_a+centerX, y_a+centerY);
		ctx.lineTo(_x_c + _x_b + centerX, _y_c + _y_b + centerY);
		ctx.moveTo(_x_b+centerX, _y_b+centerY);
		ctx.lineTo(_x_c + _x_b + centerX, _y_c + _y_b + centerY);
		ctx.lineTo(_x_f + _x_b + centerX, _y_f + _y_b + centerY);
		ctx.moveTo(x_a + centerX, y_a + centerY);
		ctx.lineTo(_x_e + _x_b + centerX, _y_e + _y_b + centerY);
		ctx.moveTo(_x_b + centerX, _y_b + centerY);
		ctx.lineTo(_x_e + _x_b + centerX, _y_e + _y_b + centerY);
		
		ctx.moveTo(_x_b + centerX, _y_b + centerY);
		ctx.lineTo(_x_f + _x_b + centerX, _y_f + _y_b + centerY);
		ctx.lineTo(_x_g + _x_e + _x_b + centerX, _y_g + _y_e + _y_b + centerY);
		ctx.lineTo(_x_e + _x_b + centerX, _y_e + _y_b + centerY);
		ctx.lineTo(_x_h + _x_e + _x_b + centerX, _y_h + _y_e + _y_b + centerY);
		ctx.moveTo(_x_g + _x_e + _x_b + centerX, _y_g + _y_e + _y_b + centerY);
		ctx.lineTo(_x_h + _x_e + _x_b + centerX, _y_h + _y_e + _y_b + centerY);
		
		ctx.stroke();

		//右側の歩行の軌跡を描く
		ctx2.lineWidth = 0;
		ctx2.fillStyle = "#cccccc";
		ctx2.beginPath();
		ctx2.arc(_x_h + _x_e + _x_b + centerX, _y_h + _y_e + _y_b + centerY, 0.5, 0, Math.PI*2, false);
		ctx2.fill();
		
	}
	
	//ロジックの処理を行うオブジェクトのグループ
	Model = 
	{
		//空のinit関数を作る
		init: function()
		{

		}
	}
			
	//Modelの中の関数(init)にプロトタイプの変数や関数を定義します
	//プロトタイプには、インスタンスで使う共有の関数や変数を設定します
	Model.init.prototype.ready = function()
	{
		//辺BC
		var dx = x_c_init - x_b;
		var dy = y_c_init - y_b;
		side_a = Math.sqrt(dx*dx+dy*dy);
		
		//辺AC
		dx = x_c_init - x_a;
		dy = y_c_init - y_a;
		side_b = Math.sqrt(dx*dx+dy*dy);
		
		//辺AE
		dx = x_e_init - x_a;
		dy = y_e_init - y_a;
		side_b2 = Math.sqrt(dx*dx+dy*dy);
		
		//辺BE
		dx = x_e_init - x_b;
		dy = y_e_init - y_b;
		side_a2 = Math.sqrt(dx*dx+dy*dy);
		
		//辺CF
		dx = x_c_init - x_f_init;
		dy = y_c_init - y_f_init;
		side_b3 = Math.sqrt(dx*dx+dy*dy);
		
		//辺BF
		dx = x_b - x_f_init;
		dy = y_b - y_f_init;
		side_c2 = Math.sqrt(dx*dx+dy*dy);
		
		//辺FG
		dx = x_g_init - x_f_init;
		dy = y_g_init - y_f_init;
		side_e2 = Math.sqrt(dx*dx+dy*dy);
		
		//辺EG
		dx = x_g_init - x_e_init;
		dy = y_g_init - y_e_init;
		side_f = Math.sqrt(dx*dx+dy*dy);
		side_h = side_f;
		
		//辺GH
		dx = x_g_init - x_h_init;
		dy = y_g_init - y_h_init;
		side_e3 = Math.sqrt(dx*dx+dy*dy);
		
		//辺EH
		dx = x_e_init - x_h_init;
		dy = y_e_init - y_h_init;
		side_g2 = Math.sqrt(dx*dx+dy*dy);
		
		//Canvasのエレメント要素を変数に代入する
		theCanvas = document.getElementById("viewArea");

		//Canvasタグで2次元描画を行ないます
		ctx = theCanvas.getContext("2d");
		
		//Canvasの中心座標を取得
		//centerX: Canvasタグの横幅の1/2を取得
		offsetX = (theCanvas.currentStyle || document.defaultView.getComputedStyle(theCanvas,'')).width;
		offsetX = Number(offsetX.replace('px',''));
		centerX =  offsetX / 2;
		//centerX =  offsetX - 55;
		
		//centerY: Canvasタグの高さの1/2を取得
		offsetY = (theCanvas.currentStyle || document.defaultView.getComputedStyle(theCanvas,'')).height;
		offsetY = Number(offsetY.replace('px',''));
		//centerY =  offsetY / 2;
		centerY = 100;

		//Canvasのエレメント要素を変数に代入する
		theCanvas2 = document.getElementById("viewArea2");

		//Canvasタグで2次元描画を行ないます
		ctx2 = theCanvas2.getContext("2d");
	}

	
	
	//Modelの中のInit関数のインスタンスを生成
	var model = new Model.init();
	//Model関数のプロトタイプにあるready関数を呼び出します
	model.ready();
	
	
	//Viewの中のInit関数のインスタンスを生成
	var view = new View.init();
	//View関数のプロトタイプにあるanimation関数を呼び出します
	view.pointInit();
	//view.animation();
}

// 各ブラウザ対応
//Safari対応で、一番外側に書く
(function()
{
    var requestAnimationFrame = window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function(callback)
            {
                setTimeout(callback, 1000 / 60);
            };

    var cancelAnimationFrame = window.cancelAnimationFrame ||
            window.webkitCancelRequestAnimationFrame ||
            window.mozCancelRequestAnimationFrame ||
            window.oCancelRequestAnimationFrame ||
            window.msCancelRequestAnimationFrame ||
            function(id)
            {
                clearTimeout(id);
            };

    window.requestAnimationFrame = requestAnimationFrame;
    window.cancelAnimationFrame = cancelAnimationFrame;
})();

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

コメントフィード

トラックバック URL : http://www.htmlcode.jp/html5%e3%81%aecanvas%e3%81%abjavascript%e3%81%a7%e3%83%86%e3%82%aa%e3%83%bb%e3%83%a4%e3%83%b3%e3%82%bb%e3%83%b3%e3%81%ae%e3%83%93%e3%83%bc%e3%82%b9%e3%83%88%e3%82%92%e5%86%8d%e7%8f%be%e3%81%99/trackback/