Large Display Size Middle Display Size Small Display Size
印刷用 概要 キーワード 著者
there is no canvas.

桜吹雪 JavaScript

四則演算と三角関数だけのプログラム

ブラウザが問題なくJavaScriptを動かしてくれていれば、このページの上部に桜吹雪が舞っているはずです。

さわやかできれいでしょう?

さくらの花びらはJavaScriptのCANVASのパスの機能で描かれています。

ある場所を中心に渦を巻くように回転しています。

遠くのものほど小さく描かれ、近くのものほど大きく描かれています。

これは3DCGの原理の式で実現しています。

原理の式は、h = x * ( s / z )、v = y * ( s / z ) です。

3次元座標 x, y, z を 2次元座標 h, v に変換する式です。

式中の s は焦点距離というものですが、s = 50; 固定で問題ありません。

この式は四則演算だけの式です。これだけで3次元の景色は表現できます。

以下はExcelで作成した説明です。(1枚の画像にしてしまいました)

以上のように、3次元空間の2次元化はわりと楽なのではと思います。

ただし、花びらの回転については三角関数が必要です。

でもそれもごく基本的な三角関数だと思うので、そんなに難しいものではないと思います。

下記のリンク先では「基本的な三角関数のプログラム」から始まって、「3次元空間のなかで三角関数を使って桜の花びら1枚を自転&公転させるプログラム」までをステップ・バイ・ステップで示しています。変更をすこしずつ見られるので、いきなり完成を示されるよりはわかりやすいかもしれません。

ステップごとの変更点を赤く示したり、変更を順次追う形にしていたりと、工夫をしているものの、詳しい説明はしていないので、理解は結局難しく、あまり良いページではないかもしれません。三角関数が分からない一般の方にわかってもらおうと思って始めたのですが、プログラムの中級の方や数学の中級の方向けかもしれません。

基本的な三角関数のプログラムからのステップ・バイ・ステップ


このファイル: javascript - sakura.html

<html lang="ja">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<title>花吹雪プログラム</title>
<script>
//utilities.
var HereDocument = /\/\*([^]*)\*\//;
//usage:
	
var s = ( function() {/* multi line */} ).toString().match( HereDocument )[ 1 ];

function $( id ) { return document.getElementById( id ); }
Array.prototype.last = function() { return this.length > 0 ? this[ this.length - 1 ] : null; };
function getcss( element, cssprop ) {
return document.defaultView.getComputedStyle( element, null ).getPropertyValue( cssprop );
}
var CON = 1 ? console.log : function() {};
var con = CON;

/*
花吹雪プログラム with 原理で3DCG
by homepage6047
2017年1月 公開
*/

//---カスタマイズ可能 変数(人間が変更)

//花びら(3Dモデル) 関係

//花びらの形状
//1行の3つの数値は3D空間内の1つの座標で、x
	
y
	
z の意味

//各行の座標を順に線で結ぶと花びらになる
var string = ( function() {/*
0
		
4
		
0

-1
		
6
		
0

-2
		
5
		
0

-2.5
	
4
		
0

-3
		
2
		
0

-3
		
0
		
0

-2
		
-3
		
0

0
		
-4.5
	
0

2
		
-3
		
0

3
		
0
		
0

3
		
2
		
0

2.5
		
4
		
0

2
		
5
		
0

1
		
6
		
0

*/} ).toString().match( HereDocument )[ 1 ];

//花びらの大きさ
var scale = .25;

//花びらの動くエリアについて
var sizeY = 30;
	
//花びらの動くエリアの縦幅
var holeR = 10;
	
//花びらの動くエリアの半径1(ドーナツの穴)
var rmax = 30;
	
//花びらの動くエリアの半径2(ドーナツの大きさ)


//カメラ関係
var s = 50;
			
//焦点距離
var zoom = 17.6;
	
//拡大
var camZ = -100;
	
//カメラの離れ位置



//---カスタマイズ不可 変数(プログラムが変更)

//花びら関係
var models = new Array();
//花びらの形状を配列に変換する
var tens = new Array();

var array = string.split( /\n/ );
for( var i = 0; i < array.length; i++ ) {
var xyz = array[ i ].split( /\t+/ );
tens[ i ] = new Array();
for( var j = 0; j < 3; j++ ) {
tens[ i ][ j ] = xyz[ j ];
}
}

//カメラ関係
var camKaitenH = 0;
	
//花びらの公転角度

//その他
var canvas;
var timerID;


function onloadx() {

//100枚の花びら 作成
/*
3Dモデルそのものは含まず、3Dモデルを描く位置などが1枚の花びらのデータ
このfor文の100を変更すれば花びらの数を変更できる。
*/
for( var i = 0; i < 100; i++ ) {
models[ i ] = new Object();
var model = models[ i ];

model.y = Math.random() * sizeY - sizeY / 2;

//水平面上での花びら配置 ドーナツ状に配置する
var r = Math.random() * rmax;
		
//ランダムな半径
var theta = Math.random() * 6.29;
	
//ランダムな角度
var x = Math.cos( theta ) * r;
		
//そのx座標
var y = Math.sin( theta ) * r;
		
//そのy座標

//中心から離す計算 (ドーナツにする)
/*
直角三角形の斜辺 下記holeR(離したい距離)
下記計算は三平方の定理を変形して、斜辺以外の辺の長さを求めている
直角三角形の斜辺以外の2つの辺の割合
1つの辺の割合
			
x / ( x + y )

もう1つの辺の割合
		
y / ( x + y )

*/
var ax = Math.sqrt( ( holeR * holeR ) * ( Math.abs(x) / ( Math.abs(x) + Math.abs(y) ) ) );
var ay = Math.sqrt( ( holeR * holeR ) * ( Math.abs(y) / ( Math.abs(x) + Math.abs(y) ) ) );
//離す距離はax,ayだが、離す方向は、x,yの方向に合わせる
x += ax * ( x >= 0 ? 1 : -1 );
y += ay * ( y >= 0 ? 1 : -1 );
/*
※中心から放射状に離す、というのは単純にx,y座標に数値を加えればよいというわけではなく、
上記の計算が必要だった。(気づくまで時間がかかった)
*/

model.x = x;
model.z = y;

//はなびらの開始時の自転 はなびらの向いている向きをばらつかせる
model.kaitenH = Math.random() * 6.28;
model.kaitenV = Math.random() * 6.28;

//公転の傾きを2種類から選択
model.katamuki = [ -0.3, 0 ][ Math.floor( Math.random() * 2 ) ];

//花びらの色を2種類から選択
model.color = [ "RGB(255,220,220)", "RGB(255,230,230)" ][ Math.floor( Math.random() * 2 ) ];

}

canvas = document.getElementById( "canvasID" ).getContext( "2d" );
timerID = setInterval( "run()", 100 );
}

function run() {

//花びらの公転を進める
camKaitenH += .05;

//花びらの自転を進める
for( var i = 0; i < models.length; i++ ) {
var model = models[ i ];
model.kaitenH += .005;
model.kaitenV += .2;
}

draw();
}

function draw() {
canvas.clearRect( 0, 0, 640, 480 );

//各花びらについて
for( var j = 0; j < models.length; j++ ) {
var model = models[ j ];
canvas.beginPath();

//1枚の花びらを構成する各点について
for( var i = 0; i < tens.length; i++ ) {
var x = tens[ i ][ 0 ];
var y = tens[ i ][ 1 ];
var z = tens[ i ][ 2 ];

//花びらの大きさ
x *= scale;
y *= scale;
z *= scale;

//花びらの自転 水平回転
var res = kaiten( 0, 0, x, z, model.kaitenH );
x = res.X;
z = res.Y;
//花びらの自転 垂直回転
var res = kaiten( 0, 0, z, y, model.kaitenV );
z = res.X;
y = res.Y;

//花びらの位置
x += model.x;
y += model.y;
z += model.z;

//花びらの公転 水平回転
var res = kaiten( 0, 0, x, z, camKaitenH );
x = res.X;
z = res.Y;
//花びらの公転 傾かせ回転(公転の傾きは2種類)
var res = kaiten( 0, 0, x, y, model.katamuki );
x = res.X;
y = res.Y;

//カメラはモデルの原点に置かず、離す
z += - camZ;

//原理で3DCG
var h = x * ( s / z );
var v = y * ( s / z );

v *= -1;

h *= zoom;
v *= zoom;

h += 320;
v += 240;

if( i == 0 )
canvas.moveTo( h, v );
else
canvas.lineTo( h, v );
}
canvas.closePath();
canvas.fillStyle = model.color;
canvas.fill();

}
}


//三角関数による座標の回転
/*
座標 x, y を centerX, centerY を中心にして、角度 theta2 だけ回転したときの座標を求める。
*/
function kaiten( centerX, centerY, x, y, theta2 ) {
x -= centerX;
y -= centerY;

var theta1 = Math.atan2( y, x );
var hankei = Math.sqrt( x * x + y * y );
x = Math.cos( theta1 + theta2 ) * hankei;
y = Math.sin( theta1 + theta2 ) * hankei;

x += centerX;
y += centerY;

return { X : x, Y : y };
}

</script>
<style>
</style>
</head>
<body id="bodyID" onload="onloadx();" style="
background-color
	
:
	
white;

">
<canvas id="canvasID" width="640" height="480" style="
border
	
:
	
solid 1px pink;

margin
	
:
	
auto;

display
	
:
	
block;

background-color
	
:
	
white;

">
there is no canvas.
</canvas>
<div style="text-align:center; color:pink;">
JavaScript
</div>
</body>
</html>
ページ制作 homepage6047


ページの上端へ (もくじ開く)