/* 【説明】 このファイルを 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; }