class App { constructor( canvasId, dir ) { this.dir = dir; this.cc = document.getElementById( canvasId ).getContext( "2d", { alpha : false } ); this.parentElement = this.cc.canvas.parentNode; console.log( this.parentElement.tagName ); this.parentElement.style.position = "relative"; this.cc.canvas.style.position = "absolute"; this.cc.canvas.style.left = "0px"; this.cc.canvas.style.top = "0px"; this.cc.canvas.style.imageRendering = "crisp-edges"; this.aspect = this.cc.canvas.height / this.cc.canvas.width; this.cc.canvasWidth = this.cc.canvas.width; this.cc.canvasHeight = this.cc.canvas.height; if( 0 ) { //レトロ風にする //ドットの粗さ this.pxW = 1; this.pxH = 2; //解像度を下げる this.cc.canvas.width /= this.pxW; this.cc.canvas.height /= this.pxH; //解像度1ピクセル当たりのピクセル数を変更 this.cc.scale( 1 / this.pxW, 1 / this.pxH ); /* 以降、this.cc.canvas.width と this.cc.canvas.height を参照している部分は、 this.cc.canvasWidth と this.cc.canvasHeight に変更してください。 */ } let onresizex = function( e ) { //canvasのサイズ let pr = this.parentElement.getBoundingClientRect(); this.cc.canvas.style.width = pr.width + "px"; this.cc.canvas.style.height = this.aspect * pr.width + "px"; this.parentElement.style.height = this.aspect * pr.width + "px"; let test = document.getElementById( "whiteareaId" ); if( test ) { test.style.paddingTop = this.aspect * pr.width + "px"; } } addEventListener( "resize", onresizex.bind( this ) ); onresizex.call( this ); this.bcc = document.createElement( "canvas" ).getContext( "2d" ); this.bcc.canvas.width = this.cc.canvasWidth; this.bcc.canvas.height = this.cc.canvasHeight; if( 0 ) { //bccが何を描いているか document.body.appendChild( this.bcc.canvas ); } this.phase = "loading"; this.anms = new Array(); this.starColors = [ "white", ]; this.starTimes = [ //上から下まで何秒かかるか (ms) 1500, 3000 ]; this.timerMs = 70; // this.timerId = setInterval( this.frame.bind( this ), this.timerMs ); //---スクロール停止対応2 ここから //どの要素を対象にしますか this.target = this.cc.canvas; //要素が何%見えたらアクティブにしますか this.targetPer = 0.5; this.isActive = false; addEventListener( "scroll", this.onscrollx.bind( this ) ); this.onscrollx(); //---スクロール停止対応2 ここまで this.execute(); } //App //App async execute() { //背景の星のオブジェクトを 50 個生成 this.stars = new Array(); for( let i = 0; i < 50; i++ ) { let star = { x : Math.random() * this.cc.canvasWidth, y : Math.random() * this.cc.canvasHeight, color : this.starColors[ Math.floor( Math.random() * this.starColors.length ) ], } let starTime = this.starTimes[ Math.floor( Math.random() * this.starTimes.length ) ]; let a = 2.5; //星の流れの傾斜 (x=1に対するyの割合) let len = Math.sqrt( Math.pow( this.cc.canvasHeight, 2 ) + Math.pow( this.cc.canvasHeight / a, 2 ) ); let add = len / ( starTime / this.timerMs ); //1timer当たりどれくらい星は流れるか star.addX = Math.sqrt( add * add * ( 1 * 1 ) / ( 1 + a * a ) ); //そのx成分 star.addY = Math.sqrt( add * add * ( a * a ) / ( 1 + a * a ) ); //そのy成分 this.stars.push( star ); } //前景 画像のプリロード let image; image = await this.loadImage( this.dir + "text1.png" ); this.text1 = new TestImage( image, 550, this.bcc ); image = await this.loadImage( this.dir + "text2.png" ); this.text2 = new TestImage( image, 550, this.bcc ); image = await this.loadImage( this.dir + "title.png" ); this.title = new TestImage( image, 640, this.bcc ); image = await this.loadImage( this.dir + "back.png" ); this.back = new TestImage( image, 550, this.bcc ); this.back.adjustX = 0; this.back.adjustY = - 35; this.elements = new Array(); this.elements.push( this.text1 ); this.elements.push( this.text2 ); this.elements.push( this.back ); this.elements.push( this.title ); //各種アニメを順次実行 let dev = 1; //基本1。デバッグ時は0.1などにすれば表示要素の切り替えを早められる。 let ms = 1000; //基本1000。デバッグ時は2000などにすれば画面効果をゆっくり見られる。 ※A let text = true; let leave = true; while( 1 ) { this.anms = new Array(); if( text ) { //最初のテキスト await this.fadeImage( this.text1, "transparent", "blue", ms ); await this.fadeImage( this.text1, "blue", "normal", ms ); await this.delay( 9000 * dev ); await this.fadeImage( this.text1, "normal","blue", ms ); await this.fadeImage( this.text1, "blue","transparent", ms ); await this.delay( 1500 * dev ); //次のテキスト this.text1.visibility = false; await this.fadeImage( this.text2, "transparent","blue", ms ); await this.fadeImage( this.text2, "blue","normal", ms ); await this.delay( 12000 * dev ); await this.fadeImage( this.text2, "normal","blue", ms ); await this.fadeImage( this.text2, "blue","transparent", ms ); await this.delay( 500 * dev ); } //ロゴ this.text2.visibility = false; await this.fadeImage( this.title, "transparent","blue", ms ); await this.fadeImage( this.title, "blue","little", ms ); await this.fadeImage( this.title, "little","normal", ms * 1.5 ); //魔法陣 await this.fadeImage( this.back, "transparent","blue", ms ); await this.fadeImage( this.back, "blue","little", ms ); await this.fadeImage( this.back, "little","normal", ms ); await this.delay( 18000 * dev ); //ここで画面完成 if( leave ) { //消す await this.fadeImage( this.back, "normal","blue", ms / 3 ); await this.fadeImage( this.back, "blue","transparent", ms / 3 ); await this.fadeImage( this.title, "normal","blue", ms / 3 ); await this.fadeImage( this.title, "blue","transparent", ms / 3 ); } await this.delay( 2000 * dev ); this.text1.visibility = true; this.text2.visibility = true; this.back.visibility = true; } this.phase = ""; } //App fadeImage( testimage, beforeMode, afterMode, ms ) { return new Promise( function( tellOk ) { testimage.beforeMode = beforeMode; testimage.afterMode = afterMode; let anm = { stavalue : 0, endvalue : testimage.spanbordermax, addvalue : null, nowvalue : null, object : testimage, member : "spanborder", tellOk : tellOk, } anm.addvalue = ( anm.endvalue - anm.stavalue ) / ( ms / this.timerMs ); anm.nowvalue = 0; this.anms.push( anm ); this.phase = "test"; }.bind( this ) ); } //App frame() { //背景の星 値を動かす for( let i = 0; i < this.stars.length; i++ ) { let star = this.stars[ i ]; star.x += star.addX; star.y += star.addY; //check. 画面外に出たか? if( star.x > this.cc.canvasWidth || star.y > this.cc.canvasHeight ) { //位置を上部へ移動 let v1 = this.cc.canvasWidth + this.cc.canvasHeight; let v2 = Math.random() * v1; if( v2 < this.cc.canvasWidth ) { //画面上部から再出発 star.x = v2; star.y = 0; } else { //画面左端から再出発 star.x = 0; star.y = v2 - this.cc.canvasWidth; } } } //前景 汎用的なアニメ処理 for( let i = 0; i < this.anms.length; i++ ) { let anm = this.anms[ i ]; anm.nowvalue += anm.addvalue; anm.object[ anm.member ] = anm.nowvalue; //check. if( anm.addvalue > 0 && anm.nowvalue >= anm.endvalue || anm.addvalue < 0 && anm.nowvalue <= anm.endvalue ) { anm.tellOk(); } } this.draw( this.cc ); } //App draw( cc ) { cc.clearRect( 0, 0, cc.canvasWidth, cc.canvasHeight ); //背景の星 描画 for( let i = 0; i < this.stars.length; i++ ) { let star = this.stars[ i ]; cc.fillStyle = star.color; cc.fillRect( star.x, star.y, 1, 2 ); } //前景 描画 switch( this.phase ) { case "loading": cc.fillText( "Loading..", 100, 100 ); break; case "test": cc.fillStyle = "green"; for( let i = 0; i < this.elements.length; i++ ) { let element = this.elements[ i ]; element.draw( cc ); } break; } } //---utl //App delay( ms ) { return new Promise( function( tellOk ) { setTimeout( tellOk, ms ); } ); } //App loadImage( src ) { let image; return new Promise( function( tellOk ) { image = new Image(); image.onload = tellOk; image.src = src; }.bind( this ) ).then( function() { console.log( src + " loaded." ); return image; }.bind( this ) ); } //---スクロール停止対応1 ここから start() { this.timerId = setInterval( this.frame.bind( this ), this.timerMs ); } stop() { clearInterval( this.timerId ); } onscrollx( e ) { var rect = this.target.getBoundingClientRect(); var targetTopP = ( rect.top ) + ( rect.height * this.targetPer ); var targetBottomP = ( rect.top + rect.height ) - ( rect.height * this.targetPer ); var windowTop = 0; var windowBottom = window.innerHeight; var overTheTop = targetBottomP < windowTop; //ウィンドウ上方へ隠れた var overTheBottom = targetTopP > windowBottom; //ウィンドウ下方へ隠れた var isVisible = ! overTheTop && ! overTheBottom; if( ! this.isActive && isVisible ) { this.isActive = true; this.start(); } else if( this.isActive && ! isVisible ) { this.isActive = false; this.stop(); } } //---スクロール停止対応1 ここまで }//App class TestImage { constructor( image, width, bcc ) { this.image = image; let height = width / image.width * image.height; this.bcc = bcc; //あとでまた使う //image を imageData に変換する(bccに描画してgetする) bcc.clearRect( 0, 0, bcc.canvas.width, bcc.canvas.height ); bcc.drawImage( image, 0, 0, width, height ); this.imagedata = bcc.getImageData( 0, 0, width, height ); this.x = 0; this.y = 0; this.adjustX = 0; this.adjustY = 0; this.spanbordermax = 47; //span border max this.spanborder = 0; //0:alldraw ~ max:alloff this.visibility = true; this.modes = { normal : { r : 1, g : 1, b : 1, a : 1 }, //通常 little : { r : 0.6, g : 0.6, b : 0.6, a : 1 }, //明るさ半分 transparent : null, //透明 blue : { r : 0, g : 0, b : 1, a : 1 }, //ブルー } } //TestImage draw( cc ) { //check. if( ! this.visibility ) return; let imagedata2; let before = this.modes[ this.beforeMode ]; let after = this.modes[ this.afterMode ]; /* 1枚の画像は「さざなみ」のように2つの描画モードが混在される。 this.spanbordermaxピクセルの内、左側がafterで、右側がbefore。 this.spanborderの値が0からthis.spanbordermaxまでアニメにより変化する。 その結果、this.spanbordermaxピクセル中のafterとbeforeの比率が変わるので、 全beforeの状態から全afterの状態へだんだん移り変わる、さざなみアニメとなる。 たとえば、this.spanbordermaxが18ドットで、this.spanborderが0だと、ドットはすべてbeforeのモードで描かれる。 this.spanborderが9だと、beforeとafterが半々。 this.spanborderが18だと、すべてafterのモードで描かれる。 たとえば、青一色から明るい色へとさざなみのように移り変わる。 */ imagedata2 = cc.createImageData( this.imagedata ); for( let y = 0; y < this.imagedata.height; y ++ ) { for( let x = 0; x < this.imagedata.width; x ++ ) { let seek = y * this.imagedata.width * 4 + x * 4; //元の画像の1ドット let r = this.imagedata.data[ seek ]; let g = this.imagedata.data[ seek + 1 ]; let b = this.imagedata.data[ seek + 2 ]; let a = this.imagedata.data[ seek + 3 ]; let isAfter = ( y * this.imagedata.width + x ) % this.spanbordermax < this.spanborder; let thismode = isAfter ? after : before; //check. (transparent) if( thismode == null ) continue; //加工後 imagedata2.data[ seek ] = r * thismode.r; imagedata2.data[ seek + 1 ] = g * thismode.g; imagedata2.data[ seek + 2 ] = b * thismode.b; imagedata2.data[ seek + 3 ] = a * thismode.a; } } //加工後のimagedataを画面に描画 //imagedataはそのままputImageすると、他の画像との重ね合わせができない(canvasの仕様)ので、 //bccに描画してからbcc.canvasをccへdrawImageする。(すると他の画像との重ね合わせができる) this.bcc.clearRect( 0, 0, this.bcc.canvas.width, this.bcc.canvas.height ); this.bcc.putImageData( imagedata2, 0, 0 ); this.x = ( cc.canvasWidth - this.imagedata.width ) / 2; //センタリング this.y = ( cc.canvasHeight - this.imagedata.height ) / 2; this.x += this.adjustX; this.y += this.adjustY; cc.drawImage( this.bcc.canvas, this.x, this.y ); } }//TestImage