このファイル: /home/web6047/www/cgi-bin/prj/20210612-RPG/作成/20210816-エフェクトの試作/web6047document - snapshot 20210829.html
1
<html>
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: + location.pathname ) }
8
</script>
9
<script>
10
function onloadx() {
11
app = new App();
12
}
13
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
27
if( metronome.changed ) {
28
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
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
49
50
let rainLen = 4;
51
52
let W = rainLen / Math.sqrt( 1 + hPerW * hPerW );
53
let H = W * hPerW;
54
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
75
if( rain.y > target.h ) rain.y = 0;
76
rain.x -= W * step;
77
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
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
100
if( metronome.changed ) {
101
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
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
129
if( metronome.changed && metronome.toggle ) {
130
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
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
168
169
let remainingMotion = 1 - opacity;
170
let remainingTimeMs = endTimeMs - Date.now();
171
let remainingDrawing = remainingTimeMs / metronormMaxMs;
172
let step = remainingMotion / remainingDrawing;
173
174
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
}
186
187
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
}
228
229
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 );
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
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
378
379
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
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
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
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
}
465
draw( cc ) {
466
cc.clearRect( 0, 0, cc.canvasWidth, cc.canvasHeight );
467
468
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
495
cc.save();
496
cc.translate( this.half * this.cellsize, this.half * this.cellsize );
497
this.player.effectiveDraw( cc );
498
cc.restore();
499
}
500
}
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>