/*
このファイルを
を使ってHTML内に置くと、
"whiteareaID"というIDを付けたHTML要素の上部にCANVASが表示され、
自動的に動作を開始します。
(最初のH2タグの位置までをCANVASの縦サイズとしています)
以下は、ある程度コメントでプログラムの案内をしています。
*/
//オブジェクトに関数を書く様式でプログラムを書いています。
app = {
uid : Date.now(), //uidと言ってもデバッグプリントやHTML要素のIDとして使っているだけのものです
//ページ読み込み完了時の関数
onload : function( e ) {
console.log( this.uid, "onload" );
//CANVAS作成など
this.timerID = null;
this.timerMS = 100;
var canvasElement = document.createElement( "canvas" );
canvasElement.id = "canvas" + this.uid;
canvasElement.innerHTML = '';
// canvasElement.style.backgroundColor = "RGB(0,32,96)";
this.p = document.getElementById( "whiteareaID" );
this.p.appendChild( canvasElement );
this.cc = document.getElementById( canvasElement.id ).getContext( "2d" );
//ccはCanvasContextの略です
//---vars1
//varsの中に星やカメラなどアプリケーションの動きに関する
//変数を置いています。「なんかいいことあるかな」と思って。
this.vars = {
image : new Image(),
pixelStart : 162,
cnt : 29,
}
this.vars.image.onload = function( e ) {
document.getElementById( "javascriptloading" ).innerHTML = "";
}
let idx = Math.floor( Math.random() * 3 ) + 1;
this.vars.image.src = "20191201-indexJS/Sheet" + idx + ".png";
this.v = new Object();
this.v.cubeMaster = {
tens : [
{ x : -1, y : 1, z : 0 },
{ x : 1, y : 1, z : 0 },
{ x : 1, y : -1, z : 0 },
{ x : -1, y : -1, z : 0 },
],
mens : [
[ 0, 1, 2, 3 ],
],
}
this.v.cubes = new Array();
let mx = 14;
let my = 5;
let lenX = 200;
for( y = 0; y < my; y++ ) {
for( x = 0; x < mx; x++ ) {
cube = new Object();
cube.master = this.v.cubeMaster;
cube.x = x * lenX - ( lenX * mx - lenX ) / 2;
cube.y = y * lenX - ( lenX * my - lenX ) / 2;
cube.z = 600;
cube.scale = 100;
cube.tensC = null;
cube.rotation = {
x : Math.random() * 3.14,
y : 0,
z : 0,
};
cube.rotationSpeed = Math.random() * 6.28 / 40 + 0.05;
cube.color = [ "blue", "red", "magenta", "green", "cyan", "yellow" ][ Math.floor( Math.random() * 7 ) ];
this.v.cubes.push( cube );
}
}
this.v.cam = {
x : 0,
y : 0,
z : -100,
s : 300,
zoom : 17.25,
}
//画面の上のほうクリックで情報表示
var app = this;
this.pixel = this.vars.pixelStart; //2とかにするとファミコンみたいな画質になります。
if( 1 ) {
this.cc.canvas.style.imageRendering = "pixelated";
this.cc.canvas.style.imageRendering = "optimizeSpeed";
}
this.onresize();
addEventListener( "resize", app.onresize.bind( app ), false );
addEventListener( "scroll", app.onscroll.bind( app ), false );
//このbindというのは、その関数(たとえばonresize)実行時、
//その関数内でthisにあたるオブジェクトを
//引数で指定したオブジェクトに変更するというものです。
//これをやらないとthisはwindowになります。
},//onload()
//---onresize
//ウィンドウリサイズ時の関数
onresize : function( e ) {
console.log( this.uid, "onresize" );
var canvasElement = this.cc.canvas;
var h2;
//homepage6047のページの最初のH2タグ(日記日付を書いているタグ)を検索
for( var i = 0; i < this.p.children.length; i++ ) {
var child = this.p.children[ i ];
if( child.tagName == "H2" ) {
h2 = child;
break;
}
}
//そのH2タグの位置までをCANVASの縦サイズとする
var pr = this.p.getBoundingClientRect();
var w = pr.width;
var h = h2.offsetTop;
with( canvasElement.style ) {
position = "absolute";
left = "0px";
top = "0px";
width = w + "px";
height = h + "px";
zIndex = -2;
}
this.cc.canvasWidth = w;
this.cc.canvasHeight = h;
//cssのwidthはそのままに、canvasのwidthをpixelで割った小さいサイズに
//するので画質が落ちます。
this.cc.canvas.width = w / this.pixel;
this.cc.canvas.height = h / this.pixel;
this.cc.scale( 1 / this.pixel, 1 / this.pixel );
this.draw( this.cc );
this.onscroll(); //このonscroll関数の中にsetInterval()があり、アニメ開始となる。
},//onresize()
//---draw
//画面描画
draw : function( cc ) {
cc.clearRect( 0, 0, cc.canvasWidth, cc.canvasHeight );
//各点を計算
for( let j = 0; j < this.v.cubes.length; j++ ) {
let cube = this.v.cubes[ j ];
cube.tensC = new Array();
for( let i = 0; i < cube.master.tens.length; i++ ) {
let ten = cube.master.tens[ i ];
let tenC = new Object();
tenC.x = ten.x * cube.scale;
tenC.y = ten.y * cube.scale;
tenC.z = ten.z * cube.scale;
//自転 - x軸回転
f = function( X, Y, theta2 ) {
let hankei = Math.sqrt( X * X + Y * Y );
let theta1 = Math.atan2( Y, X );
return {
X : Math.cos( theta1 + theta2 ) * hankei,
Y : Math.sin( theta1 + theta2 ) * hankei,
}
}
let res = f( tenC.z, tenC.y, cube.rotation.x );
tenC.z = res.X;
tenC.y = res.Y;
//移動
tenC.x += cube.x;
tenC.y += cube.y;
tenC.z += cube.z;
tenC.h = tenC.x * ( this.v.cam.s / tenC.z );
tenC.v = tenC.y * ( this.v.cam.s / tenC.z );
cube.tensC.push( tenC );
}
}
//描画
cc.save();
cc.globalAlpha = 0.5;
cc.translate( cc.canvasWidth / 2, cc.canvasHeight / 2 );
for( let j = 0; j < this.v.cubes.length; j++ ) {
let cube = this.v.cubes[ j ];
for( let i = 0; i < cube.master.mens.length; i++ ) {
let men = cube.master.mens[ i ];
cc.beginPath();
for( let k = 0; k < men.length; k++ ) {
let tenIdx = men[ k ];
let tenC = cube.tensC[ tenIdx ];
cc[ k == 0 ? "moveTo" : "lineTo" ]( tenC.h, tenC.v );
}
cc.closePath();
cc.fillStyle = cube.color;
cc.fill();
}
}
cc.restore();
},//draw()
//---onscroll
//ウィンドウがスクロールされたときの関数
onscroll : function( e ) {
//画面外ならスクリプトを停止する。画面内ならスクリプトを開始する。
//そうしないと常にパソコンのCPUを使い続ける(熱を出し続ける)ので…
var rect = this.cc.canvas.getBoundingClientRect();
var canvasTop = rect.top;
var canvasBottom = rect.top + rect.height;
var windowBottom = window.innerHeight;
var overTheTop = canvasBottom - 200 < 0;
var overTheBottom = canvasTop + 200 > windowBottom;
var isVisible = !overTheTop && !overTheBottom;
if( this.timerID && !isVisible ) this.stop();
else if( !this.timerID && isVisible ) this.start();
},
//この setInterval() でアニメ開始となる
start : function() {
console.log( this.uid, "start" );
this.timerID = setInterval( this.frame.bind( this ), this.timerMS );
// this.frame();
},
//アニメ停止
stop : function() {
console.log( this.uid, "stop" );
clearInterval( this.timerID );
this.timerID = 0;
},
//setInterval()から呼ばれる関数(映像1フレーム分の処理)
frame : function() {
for( let i = 0; i < this.v.cubes.length; i++ )
this.v.cubes[ i ].rotation.x += this.v.cubes[ i ].rotationSpeed;
this.draw( this.cc );
if( this.pixel == 1 ) return;
this.pixel = 0.2 * this.vars.cnt * this.vars.cnt + 0.8;
this.vars.cnt --;
//check.
if( this.pixel < 1 ) {
this.pixel = 1;
}
this.onresize();
},//frame()
};//app
//ページ読み込み完了で、onload関数を実行する
addEventListener( "load", app.onload.bind( app ), false );
//The End of the Script.