Skin:
[NORMAL]
[BLUE] [DOS] [LIGHT]  / コピーするための表示 / 実行
このファイル: /home/web6047/www/cgi-bin/prj/index - 201806.js
1 /*
2 【説明】
3
4 このファイルを html ファイルの script タグの src 属性で指定するだけで、動作開始するように作ったつもり。
5 目的のソフトウェアを App というクラスにまとめてある。
6 コメントで、「以下 変更しない部分」、「以下 変更する部分」のようにファイルを大きく2分してある。
7 このファイルを再利用するためです。
8
9 html の読み込みが完了した時点で、App クラスの onloadx1() 関数が呼ばれる。
10
11 onloadx1() と onloadx2() は処理的に分ける必要はないが、変更する必要のない部分と、
12 目的のソフトウェアを実装する部分とに分別するために分けた。
13
14 リサイズされたら、目的の部分(canvas 要素)はサイズを自動で合わせるようになっている。
15
16
17 【目的のソフトウェア 部分】
18
19 ページのロードが完了すると、このプログラムは画面に大きく、多数の四角形の 3DCG を描画する。
20 右上の STOP ボタンで停止と再開を行う。
21
22 アニメを行う基本的なプログラムになっている。
23
24 onloadx2() で 3DCG の各モデルの定義を行い、その後すぐに draw() している。
25 つづく start() でアニメが開始される。( setInterval() による run() の定期実行を行う)
26
27 run() は各アニメの数値の推移を行い、
28 "すい星"(大きな四角形の後に小さな四角形がパラパラと続く様子)
29 の尾の作成、消去など行い、最後に draw() している。
30
31 draw() は 3DCG の描画の基本的なプログラムが書かれている。
32
33 1. 画面に描く各四角形の4頂点の座標計算
34 2. 奥から手前の順になるようにソート(画家のアルゴリズム)
35 3. 描画
36
37 webGL やその他 3DCG などのライブラリは一切使用していません。
38
39
40 */
41
42 //---以下 変更しない部分
43
44 var idxjs = new App();
45
46 //ページ読み込み完了で開始
47 addEventListener( "load", function() {
48 var parentEL = document.getElementById( "whiteareaID" );
49 idxjs.onloadx1( parentEL ); //onloadx1は最後にonloadx2を呼んでいる
50 }, false );
51
52 function App() { //Appクラスのコンストラクタ
53 }
54
55 App.prototype.onloadx1 = function( parentEL ) {
56
57 //(※正直言うとcanvasのサイズについて多少混乱中…)
58
59 //check.
60 if( ! parentEL ) parentEL = document.body;
61
62 //canvasを置く親要素
63 this.parentEL = parentEL;
64 with( this.parentEL.style ) {
65 position = "relative";
66 zIndex = 0;
67 }
68
69 //アニメの停止ボタン 親要素の上部に配置
70 this.swEL = document.createElement( "div" );
71 this.parentEL.appendChild( this.swEL );
72 with( this.swEL.style ) {
73 border = "solid 0px black";
74 position = "absolute";
75 right = "0px";
76 top = "0px";
77 boxSizing = "border-box";
78 zIndex = 2;
79 padding = ".5em 1em";
80 }
81 this.swEL.innerHTML = "<a href=\"javascript:if( timerID ) { clearInterval( timerID ); timerID = 0;} else idxjs.start();void(0);\">STOP</a>";
82
83 //CANVAS要素 親要素の上部に配置
84 this.canvasEL = document.createElement( "canvas" );
85 this.parentEL.appendChild( this.canvasEL );
86 this.canvasEL.setAttribute( "id", "idxjs_canvasELID" );
87 with( this.canvasEL.style ) {
88 border = "solid 0px black";
89 position = "absolute";
90 left = "0px";
91 top = "0px";
92 boxSizing = "border-box";
93 zIndex = -1;
94
95 }
96
97
98 //true にするとドットがシャープになる。falseはアンチエイリアスが入る(通常)。IEは非対応
99 if( true ) {
100 this.canvasEL.style.imageRendering = "pixelated";
101 this.canvasEL.style.imageRendering = "optimizeSpeed";
102 }
103
104 this.canvas = this.canvasEL.getContext( "2d" );
105 this.onresizex();
106
107 //resize設定
108 this_closure = this;
109 addEventListener( "resize", function( e ) {
110 this_closure.onresizex( e );
111 }, false );
112
113 this.onloadx2();
114
115 }//onloadx1
116
117 App.prototype.onresizex = function( e ) {
118 //リサイズされた親要素に合わせて、サイズ変更
119 var waRect = this.parentEL.getBoundingClientRect();
120 this.canvasW = waRect.width;
121 this.canvasH = 480;
122 this.canvasEL.setAttribute( "width", this.canvasW );
123 this.canvasEL.setAttribute( "height", this.canvasH );
124
125 with( this.canvasEL.style ) {
126 width = this.canvasW;
127 height = this.canvasH;
128 }
129
130 //true にすると解像度を下げる。false は通常。
131 if( true ) {
132 var pixelsize = 2; //ドットの大きさ2~
133 this.canvasEL.style.width = this.canvasEL.width + "px"; //実物画面大きさとして
134 this.canvasEL.style.height = this.canvasEL.height + "px";
135 this.canvasEL.width /= pixelsize; //解像度として
136 this.canvasEL.height /= pixelsize;
137 this.canvas.scale( 1 / pixelsize, 1 / pixelsize );
138 }
139 }//onresizex
140
141 //---以上 変更しない部分
142
143
144 //---以下 変更する部分 目的のソフトウェア
145
146 App.prototype.onloadx2 = function() {
147 /*
148 使える変数:
149 this.canvasEL
150 this.canvasW
151 this.canvasH
152 */
153
154 //3DCGセッティング
155
156 //四角形 モデル リンク形状が参照する
157 this.squareMaster = {
158 tens : [
159 [ -1, -1, 0 ],
160 [ 1, -1, 0 ],
161 [ 1, 1, 0 ],
162 [ -1, 1, 0 ]
163 ],
164 };
165
166 //水色のすい星 リンク形状
167 this.square0 = {
168 ref : this.squareMaster,
169 scale : 100,
170 pos : xyz( 600, 0, 1000 ), //xが公転アニメの半径となる
171 rotation : xyz( 0, 0, 0 ),
172 type : 0, //0: normal, 1:tail
173 }
174
175 //四角形 リンク形状
176 this.squares = [
177 this.square0,
178 { //一枚のピンク色の四角形
179 ref : this.squareMaster,
180 scale : 75,
181 pos : xyz( 500, 0, 1000 ), //xが公転アニメの半径となる
182 rotation : xyz( 0, 0, 0 ),
183 type : 0, //0: normal, 1:tail
184 }
185 //以降、尾がrun()ごとに追加されていく
186 ]
187
188 //アニメ 他の3DCG形状と似せたオブジェクトにして扱う
189 this.animates = new Array();
190 this.animates.push( { //公転
191 pos : xyz( 0, 0, 1000 ),
192 rotation : xyz( 0, 0, 0 ),
193 rotationAdd : xyz( 0, 0.05, 0 ),
194 } );
195 this.animates.push( { //面の自転
196 pos : xyz( 0, 0, 0 ),
197 rotation : xyz( 0, 0, 0 ),
198 rotationAdd : xyz( 0, 0, -0.05 ),
199 } );
200 this.animates.push( { //公転 逆 ピンクの四角形用
201 pos : xyz( -100, 0, 1000 ),
202 rotation : xyz( 0, 0, 3.14/1.75 ),
203 rotationAdd : xyz( 0, 0.05, 0 ),
204 } );
205
206 //カメラ 他の3DCG形状と似せたオブジェクトにして扱う
207 this.camera = {
208 pos : xyz( 0, 150, 00 ),
209 s : 40,
210 zoom : 17.25,
211 rotation : xyz( -0.3, 0, -0.2 ),
212 };
213
214 mouseX = 0;
215 mouseY = 0;
216
217 this.draw();
218 this.start();
219 onmousemove = function( e ) {
220 //this is canvasEL.
221 mouseX = e.clientX;
222 mouseY = e.clientY;
223 };
224 }
225
226 App.prototype.start = function() {
227 //タイマ
228 var this_closure = this;
229 timerID = setInterval( function() {
230 this_closure.run();
231 }, 80 );
232 }
233
234 App.prototype.run = function() {
235
236 //各回転アニメを実行する
237 for( var i = 0; i < this.animates.length; i++ ) {
238 with( this.animates[ i ] ) {
239 rotation.x += rotationAdd.x;
240 rotation.y += rotationAdd.y;
241 rotation.z += rotationAdd.z;
242 //check. 変数の上限に到達しないように変数値をリセットする
243 // rotation.x %= 6.28;
244 // rotation.y %= 6.28;
245 // rotation.z %= 6.28;
246 //ただ、計算すると到達するのは 4.6237 * ( 10の300乗 ) 年後なので
247 //コメントアウト…計算は正しいはずだけどホントかな。。
248 }
249 }
250
251 //すい星の尾の誕生 run()ごとに1個誕生
252 var square = objcopy( this.square0 );
253 square.type = 1; //0:すい星本体 1:すい星の尾 という意味
254 square.cntmax = 15; //尾の残存カウント run()15回で尾は消滅
255 square.cnt = 1;
256 var rate = 70; //尾の位置ばらつき
257 square.pos.x += Math.random() * rate;
258 square.pos.y += Math.random() * rate;
259 square.pos.z += Math.random() * rate * 0.5;
260 var rate = 1; //尾の回転ばらつき
261 square.rotation.x += Math.random() * rate;
262 square.rotation.y += Math.random() * rate;
263 square.rotation.z += Math.random() * rate;
264 this.squares.push( square );
265
266 //すい星の尾の消滅チェック
267 for( var i = this.squares.length - 1; i >= 0; i-- ) {
268 var square = this.squares[ i ];
269 //check. 処理は尾のみを対象とする
270 if( square.type == 0 ) continue;
271
272 square.cnt++;
273 //check.
274 if( square.cnt >= square.cntmax ) {
275 this.squares.splice( i, 1 );
276 }
277 }
278
279 this.draw();
280 }
281
282 App.prototype.draw = function() {
283 /*
284 draw()はとても長くなった。
285
286 1. 画面に描く各四角形の4頂点の座標計算
287 2. 奥から手前の順になるようにソート
288 3. 描画
289 */
290
291 //1. 画面に描く各四角形の4頂点の座標計算
292 for( var j = 0; j < this.squares.length; j++ ) {
293 var square = this.squares[ j ];
294 var is0 = square == this.square0; //square0はすい星の尾ではなく本体
295 square.jusin = xyz( 0, 0, 0);
296 square.tensC = new Array();
297
298 for( var i = 0; i < square.ref.tens.length; i++ ) {
299 var refTen = square.ref.tens[ i ];
300
301 //四角形を構成する1つの点について
302 var x = refTen[ 0 ];
303 var y = refTen[ 1 ];
304 var z = refTen[ 2 ];
305
306 //以降この x, y, z に様々な計算(5種)を加える
307
308 //1. 四角形の大きさ
309 var a = square.type == 0 ? 1 : ( 1 / ( square.cnt * 0.75 ) ); //大きさの遅延
310 x *= square.scale * a;
311 y *= square.scale * a;
312 z *= square.scale * a;
313
314
315 //2. 四角形の初期状態 自転 (最初の設定上の自転回転)
316 var res = rotate1( x, z, square.rotation.y );
317 x = res.X;
318 z = res.Y;
319 var res = rotate1( z, y, square.rotation.x );
320 z = res.X;
321 y = res.Y;
322 var res = rotate1( x, y, square.rotation.z );
323 x = res.X;
324 y = res.Y;
325
326 //3. アニメ「自転」 (初期状態にアニメ分の回転を加える)
327 var animate = this.animates[ 1 ]; //面が自転
328 //x軸回転
329 var res = rotate2( animate.pos.z, animate.pos.y, z, y, animate.rotation.x );
330 z = res.X;
331 y = res.Y;
332 //y軸回転
333 var res = rotate2( animate.pos.x, animate.pos.z, x, z, animate.rotation.y );
334 x = res.X;
335 z = res.Y;
336 //z軸回転
337 var res = rotate2( animate.pos.x, animate.pos.y, x, y, animate.rotation.z );
338 x = res.X;
339 y = res.Y;
340
341
342 //4. 四角形の初期状態 位置 (最初の設定上の位置)
343 x += square.pos.x;
344 y += square.pos.y;
345 z += square.pos.z;
346
347 //5. アニメ「公転」 各四角形がグルグル回る
348 var animate;
349 var a;
350 if( square.type == 0 && square != this.square0 ) { //一枚のピンク色四角形
351 animate = this.animates[ 2 ]; //公転 逆
352 a = 0;
353 } else { //その他水色の四角形
354 animate = this.animates[ 0 ]; //公転
355 var b = .15; //2個目の距離
356 a = ( is0 ? 0 : ( b + square.cnt * 0.075 ) ); //距離の遅延
357 }
358 //x軸回転
359 var rotation = animate.rotation.x - ( animate.rotation.x != 0 ? a : 0 );
360 var res = rotate2( animate.pos.z, animate.pos.y, z, y, rotation );
361 z = res.X;
362 y = res.Y;
363 //y軸回転
364 var rotation = animate.rotation.y - ( animate.rotation.y != 0 ? a : 0 );
365 var res = rotate2( animate.pos.x, animate.pos.z, x, z, rotation );
366 x = res.X;
367 z = res.Y;
368 //z軸回転
369 var rotation = animate.rotation.z - ( animate.rotation.z != 0 ? a : 0 );
370 var res = rotate2( animate.pos.x, animate.pos.y, x, y, rotation );
371 x = res.X;
372 y = res.Y;
373
374 //以上の様子をカメラはどんな姿勢で捉えるか
375
376 //カメラの位置
377 x -= this.camera.pos.x;
378 y -= this.camera.pos.y;
379 z -= this.camera.pos.z;
380
381 //カメラの自転
382 var res = rotate1( x, z, -this.camera.rotation.y );
383 x = res.X;
384 z = res.Y;
385 var res = rotate1( z, y, -this.camera.rotation.x );
386 z = res.X;
387 y = res.Y;
388 var res = rotate1( x, y, -this.camera.rotation.z );
389 x = res.X;
390 y = res.Y;
391
392
393 //四角形の中心座標(重心)を平均値で求める 途中
394 square.jusin.x += x;
395 square.jusin.y += y;
396 square.jusin.z += z;
397
398 //以上が3次元について、以下は2次元について
399
400 var h = x * ( this.camera.s / z );
401 var v = -y * ( this.camera.s / z );
402
403 h *= this.camera.zoom;
404 v *= this.camera.zoom;
405
406 //この for i 文の目的がこれ
407 square.tensC[ i ] = {
408 x : x,
409 y : y,
410 z : z,
411 h : h,
412 v : v,
413 };
414 }//for i
415
416 //四角形の中心座標(重心)を平均値で求める 完了
417 square.jusin.x /= 4;
418 square.jusin.y /= 4;
419 square.jusin.z /= 4;
420
421 //check. canvasを他のHTMLの手前に描くか奥に描くか
422 if( square.jusin.z > this.animates[ 0 ].pos.z || isMouseOver( this.canvasEL ) ) { //奥へ
423 this.canvasEL.style.zIndex = -1;
424 } else { //手前へ
425 this.canvasEL.style.zIndex = 1;
426 }
427 }//for j
428
429 //2. 奥から手前の順になるようにソート (画家のアルゴリズム)
430 this.squares.sort( function( a, b ) {
431 return ( a.jusin.z < b.jusin.z ) - ( a.jusin.z > b.jusin.z );
432 } );
433
434 //3. 描画
435 with( this.canvas ) {
436 clearRect( 0, 0, this.canvasW, this.canvasH );
437
438 save();
439 translate( this.canvasEL.width, this.canvasEL.height );
440 //画面をCSSで2倍に引き伸ばししているので、
441 //canvasELのHTML属性のサイズが、画面の中心を表す。
442 //画面の中心を 0 ,0 の原点とする。
443
444 for( var j = 0; j < this.squares.length; j++ ) {
445 var square = this.squares[ j ];
446
447 beginPath();
448 for( var i = 0; i < square.tensC.length; i++ ) {
449 var tenC = square.tensC[ i ];
450 if( i == 0 ) {
451 moveTo( tenC.h, tenC.v );
452 } else {
453 lineTo( tenC.h, tenC.v );
454 }
455 }
456 closePath();
457
458 if( square.type == 0 ) { //type 0: 尾ではない 1:尾である
459 //すい星本体か、ピンクの四角形か
460 fillStyle = square == this.square0 ? rgb( 0,255,192 ) : rgb( 255,128,192 );
461 } else {
462 //すい星の尾 cntが大きいほど緑色
463 fillStyle = rgb( 0, 255, 192 * ( 1 / ( square.cnt * .5 ) ) );
464 }
465 fill();
466 }
467 restore(); //画面の中心を左上に戻す
468 }
469 }
470 function isMouseOver( element ) {
471 var r = element.getBoundingClientRect();
472 var res = mouseX >= r.left && mouseX <= r.left + r.width && mouseY >= r.top && mouseY <= r.top + r.height;
473 return res;
474 }
475 function xyz( x, y, z ) { //便利関数
476 return { x : x, y : y, z : z };
477 }
478 function rgb( r, g, b ) { //便利関数
479 return "rgb(" + r + "," + g + "," + b + ")"
480 }
481 function rotate1( x, y, theta2 ) { //数学関数
482 //0,0を原点として回転
483 var theta1 = Math.atan2( y, x );
484 var hankei = Math.sqrt( x * x + y * y );
485 var rx = Math.cos( theta1 + theta2 ) * hankei;
486 var ry = Math.sin( theta1 + theta2 ) * hankei;
487 return { X : rx, Y : ry };
488 }
489 function rotate2( cx, cy, x, y, theta2 ) { //数学関数
490 //cx,cyを原点として回転
491 x -= cx;
492 y -= cy;
493 var res = rotate1( x, y, theta2 );
494 res.X += cx;
495 res.Y += cy;
496 return res;
497 }
498 function objcopy( obj ) { //オブジェクトを簡易コピーする
499 var res;
500
501 if( obj instanceof Array ) {
502
503 res = new Array();
504 for( var i = 0; i < obj.length; i++ ) {
505 if( typeof obj[ i ] == "object" ) {
506 res[ i ] = objcopy( obj[ i ] );
507 } else {
508 res[ i ] = obj[ i ];
509 }
510 }
511
512 } else {
513
514 res = new Object();
515 for( var name in obj ) {
516 if( typeof obj[ name ] == "object" ) {
517 res[ name ] = objcopy( obj[ name ] );
518 } else {
519 res[ name ] = obj[ name ];
520 }
521 }
522 }
523 return res;
524 }
525
526 function printObj( obj ) { //デバッグ用 現在未使用
527 for( var name in obj ) {
528 if( typeof obj[ name ] == "object" ) {
529 console.log( name );
530 printObj( obj[ name ] );
531 } else {
532 console.log( name, obj[ name ] );
533 }
534 }
535 }
536 function mark( x, y, canvas ) { //デバッグ用 現在未使用
537 canvas.save();
538 canvas.fillRect( x, y, 4, 4 );
539 canvas.restore();
540 }