/* どうなっているのか? 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 ) ); } };