/*
【説明】
このファイルを html ファイルの script タグの src 属性で指定するだけで、動作開始するように作ったつもり。
目的のソフトウェアを App というクラスにまとめてある。
コメントで、「以下 変更しない部分」、「以下 変更する部分」のようにファイルを大きく2分してある。
このファイルを再利用するためです。
html の読み込みが完了した時点で、App クラスの onloadx1() 関数が呼ばれる。
onloadx1() と onloadx2() は処理的に分ける必要はないが、変更する必要のない部分と、
目的のソフトウェアを実装する部分とに分別するために分けた。
リサイズされたら、目的の部分(canvas 要素)はサイズを自動で合わせるようになっている。
【目的のソフトウェア 部分】
ページのロードが完了すると、このプログラムは画面に大きく、多数の四角形の 3DCG を描画する。
右上の STOP ボタンで停止と再開を行う。
アニメを行う基本的なプログラムになっている。
onloadx2() で 3DCG の各モデルの定義を行い、その後すぐに draw() している。
つづく start() でアニメが開始される。( setInterval() による run() の定期実行を行う)
run() は各アニメの数値の推移を行い、
"すい星"(大きな四角形の後に小さな四角形がパラパラと続く様子)
の尾の作成、消去など行い、最後に draw() している。
draw() は 3DCG の描画の基本的なプログラムが書かれている。
1. 画面に描く各四角形の4頂点の座標計算
2. 奥から手前の順になるようにソート(画家のアルゴリズム)
3. 描画
webGL やその他 3DCG などのライブラリは一切使用していません。
*/
//---以下 変更しない部分
var idxjs = new App();
App.prototype.onloadx = function( e ) {
this.onloadxCount++;
console.log( this.onloadxCount + " / " + this.onloadxCountMax );
//check.
if( this.onloadxCount == this.onloadxCountMax ) {
var parentEL = document.getElementById( "whiteareaID" );
this.onloadx1( parentEL ); //onloadx1は最後にonloadx2を呼んでいる
}
};
//ページ読み込み完了で開始
idxjs.images = {
"pink" : "imgs of index/20180716/pink.png",
"orange" : "imgs of index/20180716/orange.png",
"blue" : "imgs of index/20180716/blue.png",
"white" : "imgs of index/20180716/white.png",
"guide" : "imgs of index/20180716/fig2.png",
}
idxjs.onloadxCount = 0;
idxjs.onloadxCountMax = Object.keys( idxjs.images ).length + 1;
for( var name in idxjs.images ) {
var src = idxjs.images[ name ];
idxjs.images[ name ] = new Image();
idxjs.images[ name ].onload = idxjs.onloadx.bind( idxjs );
idxjs.images[ name ].src = src;
}
addEventListener( "load", idxjs.onloadx.bind( idxjs ) );
function App() { //Appクラスのコンストラクタ
}
App.prototype.onloadx1 = function( parentEL ) {
console.log( "onloadx1" );
//(※正直言うとcanvasのサイズについて多少混乱中…)
//check.
if( ! parentEL ) parentEL = document.body;
//canvasを置く親要素
this.parentEL = parentEL;
with( this.parentEL.style ) {
position = "relative";
zIndex = 0;
}
//アニメの停止ボタン 親要素の上部に配置
this.swEL = document.createElement( "div" );
this.parentEL.appendChild( this.swEL );
with( this.swEL.style ) {
border = "solid 0px black";
position = "absolute";
right = "0px";
top = "0px";
boxSizing = "border-box";
zIndex = 2;
padding = ".5em 1em";
}
this.swEL.innerHTML = "STOP
";
this.swEL.innerHTML += 'SPEED';
//CANVAS要素 親要素の上部に配置
this.canvasEL = document.createElement( "canvas" );
this.parentEL.appendChild( this.canvasEL );
this.canvasEL.setAttribute( "id", "idxjs_canvasELID" );
with( this.canvasEL.style ) {
border = "solid 0px black";
position = "absolute";
left = "0px";
top = "0px";
boxSizing = "border-box";
zIndex = -1;
}
//true にするとドットがシャープになる。falseはアンチエイリアスが入る(通常)。IEは非対応
if( true ) {
this.canvasEL.style.imageRendering = "pixelated";
this.canvasEL.style.imageRendering = "optimizeSpeed";
}
this.canvas = this.canvasEL.getContext( "2d" );
this.onresizex();
//resize設定
this_closure = this;
addEventListener( "resize", function( e ) {
this_closure.onresizex( e );
}, false );
this.onloadx2();
}//onloadx1
App.prototype.onresizex = function( e ) {
//リサイズされた親要素に合わせて、サイズ変更
var waRect = this.parentEL.getBoundingClientRect();
this.canvasW = waRect.width;
this.canvasH = 480;
this.canvasEL.setAttribute( "width", this.canvasW );
this.canvasEL.setAttribute( "height", this.canvasH );
with( this.canvasEL.style ) {
width = this.canvasW;
height = this.canvasH;
}
//true にすると解像度を下げる。false は通常。
if( false ) {
var pixelsize = 2; //ドットの大きさ2~
this.canvasEL.style.width = this.canvasEL.width + "px"; //実物画面大きさとして
this.canvasEL.style.height = this.canvasEL.height + "px";
this.canvasEL.width /= pixelsize; //解像度として
this.canvasEL.height /= pixelsize;
this.canvas.scale( 1 / pixelsize, 1 / pixelsize );
}
}//onresizex
//---以上 変更しない部分
//---以下 変更する部分 目的のソフトウェア
App.prototype.onloadx2 = function() {
console.log( "onloadx2" );
/*
使える変数:
this.canvasEL
this.canvasW
this.canvasH
*/
this.gear = 2;
this.objects = [
{
image : this.images.pink,
x : 30 + 150 -24,
y : 30 + 150 -24,
w : 300,
h : 300,
hw : null,
hh : null,
t : 0,
add : 0.45,
},
{
image : this.images.orange,
x : 300 + 150 - 90,
y : 30 + 150 + 90,
w : 240,
h : 240,
hw : null,
hh : null,
t : 0,
add : -0.60,
},
{
image : this.images.white,
x : 300,
y : 50,
w : 110,
h : 110,
hw : null,
hh : null,
t : 0,
add : -1,
},
{
image : this.images.blue,
x : 410,
y : 80,
w : 160,
h : 160,
hw : null,
hh : null,
t : 0,
add : 0.65,
},
]
for( var i = 0; i < this.objects.length; i++ ) {
with( this.objects[ i ] ) {
hw = Math.round( w / 2 );
hh = Math.round( h / 2 );
}
}
this.draw( this.canvas );
this.start();
}
App.prototype.start = function() {
//タイマ
var this_closure = this;
timerID = setInterval( function() {
this_closure.run();
}, 80 );
}
App.prototype.run = function() {
for( var i = 0; i < this.objects.length; i++ ) {
with( this.objects[ i ] ) {
t += add * [ .23, .15, .1, .05, .01 ][ this.gear ];
}
}
this.draw( this.canvas );
}
App.prototype.draw = function( cc ) {
/*
*/
cc.clearRect( 0, 0, this.canvasW, this.canvasH );
// cc.drawImage( this.images.guide, 32, 0 );
for( var i = 0; i < this.objects.length; i++ ) {
cc.save();
with( this.objects[ i ] ) {
cc.translate( x, y );
cc.rotate( t );
cc.drawImage( image, -hw, -hh, w, h );
}
cc.restore();
}
cc.fillText( "Flower Gear", 0, 16 );
}
function rotate1( x, y, theta2 ) { //数学関数
//0,0を原点として回転
var theta1 = Math.atan2( y, x );
var hankei = Math.sqrt( x * x + y * y );
var rx = Math.cos( theta1 + theta2 ) * hankei;
var ry = Math.sin( theta1 + theta2 ) * hankei;
return { X : rx, Y : ry };
}
function rotate2( cx, cy, x, y, theta2 ) { //数学関数
//cx,cyを原点として回転
x -= cx;
y -= cy;
var res = rotate1( x, y, theta2 );
res.X += cx;
res.Y += cy;
return res;
}
function objcopy( obj ) { //オブジェクトを簡易コピーする
var res;
if( obj instanceof Array ) {
res = new Array();
for( var i = 0; i < obj.length; i++ ) {
if( typeof obj[ i ] == "object" ) {
res[ i ] = objcopy( obj[ i ] );
} else {
res[ i ] = obj[ i ];
}
}
} else {
res = new Object();
for( var name in obj ) {
if( typeof obj[ name ] == "object" ) {
res[ name ] = objcopy( obj[ name ] );
} else {
res[ name ] = obj[ name ];
}
}
}
return res;
}