/*
どうなっているのか?
App()
App.onloadx() htmlのonloadで呼ばれる。画像のプリロードからも呼ばれ、loadをカウントしてる。
App.onloadx1() htmlとimageのloadが完了したら呼ばれる。固定的な内容。onloadx2を呼ぶ。
App.onresizex() 固定的な内容。通常は変更しないと思う。
App.onloadx2() ★ここから書く★
App.start() stopをクリックした後再開するとき呼ばれる。
App.run() 書いたもの。消しても良い
App.draw() 書いたもの。消しても良い
【説明】
このファイルを 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 などのライブラリは一切使用していません。
*/
//---以下 変更しない部分
/*
→ relative, z-index=0; に変更される
STOPやSPEEDなどのリンク
自動作成される
自動作成される
*/
var indexjs = 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を呼んでいる
}
};
//ページ読み込み完了で開始
//---画像プリロード
indexjs.images = {
"red" : "20180901-indexJS/imgs/red.png",
"green" : "20180901-indexJS/imgs/green.png",
"blue" : "20180901-indexJS/imgs/blue.png",
"mountainLeft" : "20180901-indexJS/imgs/mountainLeft.png",
"title" : "20180901-indexJS/imgs/title.png",
}
indexjs.onloadxCount = 0;
indexjs.onloadxCountMax = Object.keys( indexjs.images ).length + 1;
for( var name in indexjs.images ) {
var src = indexjs.images[ name ];
indexjs.images[ name ] = new Image();
indexjs.images[ name ].onload = indexjs.onloadx.bind( indexjs );
indexjs.images[ name ].src = src;
}
addEventListener( "load", indexjs.onloadx.bind( indexjs ) );
function App() { //Appクラスのコンストラクタ
this.name = "test";
}
App.prototype.onloadx1 = function( parentEL ) {
/*
固定的な内容。
*/
console.log( "onloadx1()" );
this.speedstep = [ 10, 75, 200, 500 ];
this.timerMS = this.speedstep[ 1 ];
//(※正直言うとcanvasのサイズについて多少混乱中…)
//check.
if( ! parentEL ) parentEL = document.getElementsByTagName( "body" )[ 0 ];
//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 = "";
this.swEL.innerHTML += "STOP
";
this.swEL.innerHTML += 'SPEED';
//CANVAS要素 親要素の上部に配置
this.canvasEL = document.createElement( "canvas" );
this.parentEL.appendChild( this.canvasEL );
this.canvasEL.setAttribute( "id", "indexjs_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.lowmode = true;
this.cc = this.canvasEL.getContext( "2d" );
this.onresizex();
//resize設定
addEventListener( "resize", ( function( e ) { this.onresizex( e ); } ).bind( this ), false );
//canvasが画面外に出たらCPUパワーを抑えるために停止する処理
addEventListener( "scroll", function( e ) {
var scrollEL = document.documentElement ? document.documentElement : document.body;
var r = indexjs.cc.canvas.getBoundingClientRect();
if( scrollEL.scrollTop + r.top + r.height / 2 < 0 )
indexjs.stop();
else
if( ! indexjs.timerID ) indexjs.start();
}, false );
this.onloadx2();
}//onloadx1
App.prototype.onresizex = function( e ) {
console.log( "onresizex()" );
//リサイズされた親要素に合わせて、サイズ変更
var wa = this.parentEL;
var waRect = wa.getBoundingClientRect();
this.canvasW = Math.round( waRect.width );
//最初のh2タグに合わせる
var htags = document.getElementsByTagName( "h2" );
if( htags.length > 0 ) {
this.canvasH = Math.round( htags[ 0 ].getBoundingClientRect().top - wa.getBoundingClientRect().top ) - 16;
} else {
this.canvasH = 480;
}
this.canvasEL.style.width = this.canvasW + "px";
this.canvasEL.style.height = this.canvasH + "px";
this.canvasEL.setAttribute( "width", this.canvasW );
this.canvasEL.setAttribute( "height", this.canvasH );
//true にすると解像度を下げる。false は通常。
if( this.lowmode ) {
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.cc.scale( 1 / pixelsize, 1 / pixelsize );
}
console.log( this.canvasW, this.canvasH );
if( e ) this.draw();
}//onresizex
//---以上 変更しない部分
//---以下 変更する部分 目的のソフトウェア
App.prototype.onloadx2 = function() {
/*
基本的にはここから書く
*/
console.log( "onloadx2()" );
this.layers = new Array();
//奥
var layer = new Object();
layer.image = this.images.blue;
layer.px = 0;
layer.py = this.canvasH - layer.image.height;
layer.addX = 4;
layer.width = layer.image.width + 50;
this.layers.push( layer );
//中
var layer = new Object();
layer.image = this.images.green;
layer.px = 0;
layer.py = this.canvasH - layer.image.height;
layer.addX = 8;
layer.width = layer.image.width + 100;
this.layers.push( layer );
//手前
var layer = new Object();
layer.image = this.images.red;
layer.px = 0;
layer.py = this.canvasH - layer.image.height;
layer.addX = 16;
layer.width = layer.image.width * 1.7;
this.layers.push( layer );
this.onloadx3();
};
App.prototype.onloadx3 = function() {
/*
次の手順で呼ばれる。
SPEED リンク → resetメソッド → onloadx3メソッド
SPEEDリンクは indexjs.timerMS の値を変更する。
*/
console.log( "onloadx3()" );
this.draw();
this.start();
}
//--- タイマ制御系
App.prototype.start = function() {
console.log( "start()", this.timerMS );
this.timerID = setInterval( this.run.bind( this ), this.timerMS );
};
App.prototype.stop = function() {
if( this.timerID ) {
console.log( "stop()" );
clearInterval( this.timerID );
this.timerID = null;
}
};
App.prototype.reset = function() {
this.stop();
this.onloadx3();
};
App.prototype.run = function() {
for( var i = 0; i < this.layers.length; i++ ) {
var layer = this.layers[ i ];
layer.px += layer.addX;
//check.
if( layer.px > 0 ) {
layer.px = -layer.width;
}
}
this.draw();
};
App.prototype.draw = function() {
var cc = this.cc;
cc.fillStyle = "blue";
cc.fillRect( 0,0,this.canvasW, this.canvasH );
for( var i = 0; i < this.layers.length; i++ ) {
var layer = this.layers[ i ];
var cnt = 0;
while( 1 ) {
var gx = layer.px + layer.width * cnt;
//check.
if( gx > this.canvasW ) break;
cc.drawImage( layer.image, gx, layer.py );
cnt++;
}
}
cc.drawImage( this.images.mountainLeft, 0, this.canvasH - this.images.mountainLeft.height );
var x = ( this.canvasW - this.images.title.width ) / 2;
var y = ( this.canvasH - this.images.title.height ) / 2;
cc.drawImage( this.images.title, x + 65, y );
cc.fillStyle = "black";
var step = 4;
for( var i = 0; i < Math.round( this.canvasH / step ); i++ ) {
cc.fillRect( 0, i * step, this.canvasW, Math.floor( step / 2 ) );
}
};