Skin:
[NORMAL]
[BLUE] [DOS] [LIGHT]  / コピーするための表示 / 実行
このファイル: /home/web6047/www/cgi-bin/prj/20210612-RPG/作成/20210816-エフェクトの試作/web6047document - snapshot 20210829.html
1 <html><!--ESCAPEPROCESS-->
2 <head>
3 <meta content="text/html; charset=UTF-8" http-equiv="content-type">
4 <script>
5 console.clear();
6 onclick = function() { location.reload( true ) }
7 ondblclick = function() { location.assign( "https://192.168.11.3" + location.pathname ) }
8 </script>
9 <script>
10 function onloadx() {
11 app = new App();
12 }
13 //---Effectz
14 class Effectz {
15 constructor( app ) {
16 this.app = app;
17 }
18
19 async zoom( target, metronomeMaxMs, fromScale, toScale, endTimeMs ) {
20 return new Promise( function( promiseOk ) {
21 let dir = fromScale < toScale ? 1 : -1;
22 let nowScale = fromScale;
23 let metronome = this.app.startMetronome( metronomeMaxMs );
24 target.beforeEffects.push( function( cc ) {
25 cc.scale( nowScale, nowScale );
26 //check.
27 if( metronome.changed ) {
28 //残りの動作量を残りの描画回数で割る。それをstepとする。
29 //これは毎回計算しなおす。(ズレを吸収する)
30 let remainingMotion = toScale - nowScale; //残りの動作量
31 let remainingTimeMs = endTimeMs - Date.now(); //残り時間
32 let remainingDrawing = remainingTimeMs / metronomeMaxMs;//残り描画
33 let step = remainingMotion / remainingDrawing;
34
35 nowScale += step;
36 //check.
37 if( ( toScale - nowScale ) * dir <= 0 ) {
38 promiseOk();
39 this.app.endMetronome( metronome );
40 return true;
41 }
42 }
43
44 }.bind( this ) );
45 }.bind( this ) );
46 }
47 startRain( target, metronomeMaxMs, step, hPerW ) {
48 //hPerW 雨粒の傾き
49
50 let rainLen = 4; //雨粒の長さ
51 //雨粒の長さと傾きが作る三角形の
52 let W = rainLen / Math.sqrt( 1 + hPerW * hPerW ); //底辺
53 let H = W * hPerW; //高さ
54 //雨の傾きによる三角状の空欄をなくすための、target.w増分
55 let plusW = target.h / hPerW;
56
57 let rains = new Array();
58 for( let i = 0; i < 100; i++ ) {
59 rains[ i ] = {
60 x : Math.floor( Math.random() * target.w ),
61 y : Math.floor( Math.random() * target.h ),
62 }
63 }
64 let metronome = this.app.startMetronome( metronomeMaxMs );
65 let func = function( cc ) {
66 cc.strokeStyle = "cyan";
67 cc.lineWidth = 0.5;
68 for( let rain of rains ) {
69 cc.beginPath();
70 cc.moveTo( rain.x, rain.y );
71 cc.lineTo( rain.x - W, rain.y + H );
72 cc.stroke();
73 rain.y += H * step;
74 //check.
75 if( rain.y > target.h ) rain.y = 0;
76 rain.x -= W * step;
77 //check.
78 if( rain.x < 0 ) {
79 rain.x = Math.floor( Math.random() * target.w + plusW );
80 rain.y = 0;
81 }
82 }
83 }.bind( this );
84 target.afterEffects.push( func );
85
86 this.app.effectz.endRain = function() {
87 target.afterEffects.splice( target.afterEffects.indexOf( func ), 1 );
88 }
89 }
90 //---rotate
91 async rotate( target, metronomeMaxMs, maxTheta, endTimeMs ) {
92 return new Promise( function( promiseOk ) {
93
94 let theta = 0;
95 let metronome = this.app.startMetronome( metronomeMaxMs );
96
97 target.beforeEffects.push( function( cc ) {
98 cc.rotate( theta );
99 //check.
100 if( metronome.changed ) {
101 //残りの動作量を残りの描画回数で割る。それをstepとする。
102 //これは毎回計算しなおす。(ズレを吸収する)
103 let remainingMotion = maxTheta - theta; //残りの動作量
104 let remainingTimeMs = endTimeMs - Date.now(); //残り時間
105 let remainingDrawing = remainingTimeMs / metronomeMaxMs;//残り描画
106 let step = remainingMotion / remainingDrawing;
107 theta += step;
108 //check.
109 if( theta >= maxTheta ) {
110 promiseOk();
111 this.app.endMetronome( metronome );
112 return true;
113 }
114 }
115
116 }.bind( this ) );
117 }.bind( this ) );
118 }
119 async quake( target, width, speed, endTimeMs ) {
120 return new Promise( function( promiseOk ) {
121
122 let metronome = this.app.startMetronome( speed / 2 );
123 target.beforeEffects.push( function( cc ) {
124
125 let x = metronome.toggle ? 0 : width;
126 cc.translate( x, 0 );
127
128 //check.
129 if( metronome.changed && metronome.toggle ) {
130 //check.
131 if( Date.now() >= endTimeMs ) {
132 promiseOk();
133 this.app.endMetronome( metronome );
134 return true;
135 }
136 }
137 }.bind( this ) );
138 }.bind( this ) );
139 }
140 async flash( target, color, endTimeMs ) {
141
142 return new Promise( function( promiseOk ) {
143 let metronome = this.app.startMetronome( 0 );
144 target.afterEffects.push( function( cc ) {
145 if( metronome.toggle ) {
146 cc.fillStyle = color;
147 cc.fillRect( 0, 0, target.w, target.h );
148 }
149 //check.
150 if( Date.now() >= endTimeMs ) {
151 promiseOk();
152 this.app.endMetronome( metronome );
153 return true;
154 }
155 }.bind( this ) );
156 }.bind( this ) );
157 }
158 async fadeout( target, metronormMaxMs, color, endTimeMs ) {
159 return new Promise( function( promiseOk ) {
160 let opacity = 0;
161 let metronome = this.app.startMetronome( metronormMaxMs );
162 target.afterEffects.push( function( cc ) {
163 cc.globalAlpha = opacity;
164 cc.fillStyle = color;
165 cc.fillRect( 0, 0, target.w, target.h );
166 if( metronome.changed ) {
167 //残りの動作量を残りの描画回数で割る。それをstepとする。
168 //これは毎回計算しなおす。(ズレを吸収する)
169 let remainingMotion = 1 - opacity; //残りの動作量
170 let remainingTimeMs = endTimeMs - Date.now(); //残り時間
171 let remainingDrawing = remainingTimeMs / metronormMaxMs;//残り描画
172 let step = remainingMotion / remainingDrawing;
173
174 //check.
175 if( opacity >= 1 ) {
176 promiseOk();
177 this.app.endMetronome( metronome );
178 return true;
179 }
180 opacity += step;
181 }
182 }.bind( this ) );
183 }.bind( this ) );
184 }
185 }//Effectz
186
187 //---Drawing
188 class Drawing {
189 constructor( x, y, w, h ) {
190 this.x = x;
191 this.y = y;
192 this.cx = w / 2;
193 this.cy = h / 2;
194 this.w = w;
195 this.h = h;
196 this.afterEffects = new Array();
197 this.beforeEffects = new Array();
198 this.backgroundColor = "transparent";
199 }
200 draw( cc ) {
201 }
202 effectiveDraw( cc ) {
203 cc.save();
204 cc.fillStyle = this.backgroundColor;
205 cc.fillRect( 0, 0, this.w, this.h );
206
207 cc.translate( this.cx, this.cy );
208
209 for( let i = this.beforeEffects.length - 1; i >= 0; i-- ) {
210 let effect = this.beforeEffects[ i ];
211 if ( effect.call( this, cc ) ) {
212 this.beforeEffects.splice( i, 1 );
213 }
214 }
215
216 cc.translate( -this.cx, -this.cy );
217 this.draw( cc );
218
219 for( let i = this.afterEffects.length - 1; i >= 0; i-- ) {
220 let effect = this.afterEffects[ i ];
221 if ( effect.call( this, cc ) ) {
222 this.afterEffects.splice( i, 1 );
223 }
224 }
225 cc.restore();
226 }
227 }//Drawing
228
229 //---App
230
231 class App extends Drawing {
232 constructor() {
233
234 let cc = document.getElementById( "test" ).getContext( "2d", { alpha : false } );
235
236 let cols = 16;
237 let half = Math.floor( cols / 2 );
238 let logiczoom = 2;
239 cc.canvasWidth = ( 256 ) / logiczoom; //プログラム上の論理的サイズ
240 cc.canvasHeight = ( 256 ) / logiczoom;
241 cc.canvas.width = cc.canvasWidth;
242 cc.canvas.height = cc.canvasHeight;
243 if( 1 ) cc.canvas.style.imageRendering = "crisp-edges"; //アンチエイリアス
244 if( 1 ) {
245 let mozaic = .5; //見た目の粒の大きさ
246 cc.canvas.width = cc.canvasWidth / mozaic * 4; //物理的
247 cc.canvas.height = cc.canvasHeight / mozaic * 4;
248 cc.scale( 4 / mozaic, 4 / mozaic ); //物理1ピクセル当たりの論理ピクセル数
249 }
250 let cellsize = cc.canvasWidth / 16;
251
252 super( 0, 0, cc.canvasWidth, cc.canvasHeight );
253
254 this.backgroundColor = "black";
255
256 this.cc = cc;
257 this.cols = cols;
258 this.half = half;
259 this.cellsize = cellsize;
260
261 this.name = "app";
262
263 this.effectz = new Effectz( this );
264
265 this.colors = [
266 "black",
267 "blue",
268 "red",
269 "magenta",
270 "green",
271 "cyan",
272 "yellow",
273 "white",
274 ]
275
276 this.map = new Array();
277 for( let y = 0; y < 100; y++ ) {
278 this.map[ y ] = new Array();
279 for( let x = 0; x < 100; x++ ) {
280 this.map[ y ][ x ] = this.colors[ Math.floor( Math.random() * 8 ) ];
281 }
282 }
283
284 //---stories
285 this.stories = {
286 "5,0" : this.story1,
287 "5,2" : this.story2,
288 "5,4" : this.story3,
289 "5,6" : this.story4,
290 "5,8" : this.story5,
291 "0,3" : this.story6,
292 }
293 this.metronomes = new Array();
294
295 this.scrolling = false;
296 this.addX = 0;
297 this.addY = 0;
298 this.tweakX = 0;
299 this.tweakY = 0;
300
301 this.px = 0;
302 this.py = 0;
303
304 this.keys = new Array();
305 this.keyLock = false;
306
307 onkeydown = function( e ) {
308 if( ! this.keys.includes( e.which ) ) this.keys.push( e.which );
309 }.bind( this );
310 onkeyup = function( e ) {
311 if( this.keys.includes( e.which ) )
312 this.keys.splice( this.keys.indexOf( e.which ), 1 );
313 }.bind( this );
314
315 this.player = new Drawing( 0, 0, this.cellsize, this.cellsize );
316 this.player.draw = function( cc ) {
317 cc.beginPath();
318 cc.arc( this.cellsize / 2, this.cellsize / 2, this.cellsize / 2, 0, 6.28, false );
319 cc.closePath();
320 cc.fillStyle = "blue";
321 cc.fill();
322 cc.strokeStyle = "white";
323 cc.stroke();
324 }.bind( this );
325
326
327 this.timeMs_bak = 0;
328 this.frame( 0 );
329 }
330 startMetronome( maxMs ) {
331 let metronome = {
332 maxMs : maxMs,
333 nowMs : 0,
334 changed : false,
335 toggle : false,
336 }
337 this.metronomes.push( metronome );
338 return metronome;
339 }
340 endMetronome( metronome ) {
341 this.metronomes.splice( this.metronomes.indexOf( metronome ), 1 );
342 }
343 sensKey( key ) {
344 this.addX = ( key == 39 ) - ( key == 37 );
345 this.addY = ( key == 40 ) - ( key == 38 );
346
347 if( this.addX || this.addY ) {
348 this.scrolling = true;
349
350 }
351 }
352 patch( position, size ) {
353 return ( position + size ) % size;
354 }
355
356 async story1() {
357 if( ! confirm( "フラッシュします。「ポケモンショック(光過敏性発作)」について、「危ないんじゃないか」と思う人はキャンセルを押して回避してください。" ) ) {
358 this.keys.length = 0;
359 return;
360 } else {
361 this.keys.length = 0;
362 }
363
364 this.keyLock = true;
365 let nowMs = Date.now();
366 this.effectz.quake( this, 2, 50, nowMs + 4000 );
367 await this.effectz.flash( this, "transparent", nowMs + 1000 );
368 await this.effectz.flash( this, "red", nowMs + 2000 );
369 await this.effectz.flash( this, "yellow", nowMs + 3000 );
370 await this.effectz.flash( this, "white", nowMs + 4000 );
371 this.keyLock = false;
372 }
373
374 async story2() {
375 this.keyLock = true;
376 await this.effectz.quake( this.player, 2, 50, Date.now() + 800 );
377 // await this.effectz.flash( this.player, "red", 50 );
378 // await this.effectz.flash( this.player, "yellow", 50 );
379 // await this.effectz.flash( this.player, "white", 50 );
380 this.keyLock = false;
381 }
382
383 async story3() {
384 this.keyLock = true;
385 await this.effectz.rotate( this, 50, 6.28, Date.now() + 3000 );
386 this.keyLock = false;
387 }
388
389 async story4() {
390 this.keyLock = true;
391 let metronormMaxMs = 60;
392 let nowMs = Date.now();
393 this.effectz.rotate( this, metronormMaxMs, 6.28, nowMs + 3000 );
394 await this.effectz.zoom( this, metronormMaxMs, 1, 0, nowMs + 1500 );
395 await this.effectz.zoom( this, metronormMaxMs, 0, 1, nowMs + 3000 );
396 this.keyLock = false;
397 }
398
399 async story5() {
400 if( this.effectz.endRain ) {
401 this.effectz.endRain();
402 delete this.effectz.endRain;
403 } else
404 this.effectz.startRain( this, 100, .5, 2 );
405 }
406
407 async story6() {
408 this.keyLock = true;
409 let metronormMaxMs = 100;
410 let nowMs = Date.now();
411 await this.effectz.fadeout( this, metronormMaxMs, "black", nowMs + 1000 );
412 // await this.effectz.fadein( this, metronormMaxMs, "black", nowMs + 3000 );
413 this.keyLock = false;
414 }
415
416 frame( timeMs ) {
417
418 let diffMs = timeMs - this.timeMs_bak;
419 this.timeMs_bak = timeMs;
420
421 for( let metronome of this.metronomes ) {
422 metronome.nowMs += diffMs;
423 //check.
424 if( metronome.nowMs >= metronome.maxMs ) {
425 metronome.nowMs = 0;
426 metronome.changed = true;
427 metronome.toggle = ! metronome.toggle;
428 } else {
429 metronome.changed = false;
430 }
431 }
432
433 if( this.scrolling ) {
434 this.tweakX -= this.addX;
435 this.tweakY -= this.addY;
436 if( Math.abs( this.tweakX ) == this.cellsize ) {
437 this.tweakX = 0;
438 this.scrolling = false;
439 this.px += this.addX;
440 this.px = this.patch( this.px, 100 );
441 }
442 if( Math.abs( this.tweakY ) == this.cellsize ) {
443 this.tweakY = 0;
444 this.scrolling = false;
445 this.py += this.addY;
446 this.py = this.patch( this.py, 100 );
447 }
448
449 //check.
450 if( this.scrolling == false ) {
451 let name = this.px + "," + this.py;
452 if( this.stories[ name ] )
453 this.stories[ name ].call( this );
454 }
455 } else if( ! this.keyLock ) {
456 for( let key of this.keys ) {
457 this.sensKey( key );
458 }
459 }
460
461 this.effectiveDraw( this.cc );
462
463 requestAnimationFrame( this.frame.bind( this ) );
464 }//frame()
465 draw( cc ) {
466 cc.clearRect( 0, 0, cc.canvasWidth, cc.canvasHeight );
467
468 //map
469 cc.save();
470 cc.translate( this.tweakX, this.tweakY );
471 for( let y = 0; y < this.cols; y++ ) {
472 let gy = y * this.cellsize;
473 let my = this.patch( this.py - this.half + y, 100 );
474 for( let x = 0; x < this.cols; x++ ) {
475 let gx = x * this.cellsize;
476 let mx = this.patch( this.px - this.half + x, 100 );
477 cc.fillStyle = this.map[ my ][ mx ];
478 cc.fillRect( gx, gy, this.cellsize, this.cellsize );
479
480 let name = mx + "," + my;
481 if( this.stories[ name ] ) {
482
483 cc.fillStyle = "black";
484 cc.fillRect( gx, gy, this.cellsize, this.cellsize );
485 cc.fillStyle = "red";
486 cc.fillRect( gx + 1, gy + 1, this.cellsize - 2, this.cellsize - 2 );
487 cc.fillStyle = "black";
488 cc.fillRect( gx + 2, gy + 2, this.cellsize - 4, this.cellsize - 4 );
489 }
490 }
491 }
492 cc.restore();
493
494 //player
495 cc.save();
496 cc.translate( this.half * this.cellsize, this.half * this.cellsize );
497 this.player.effectiveDraw( cc );
498 cc.restore();
499 }
500 }//App
501
502 </script>
503 <style>
504 </style>
505 </head>
506 <body onload="onloadx()">
507 <canvas id="test" style="
508 object-fit : contain;
509 width : 100%;
510 height : 100%;
511 ">There is no canvas.</canvas>
512 </body>
513 </html>