@guinetik/gcanvas 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CNAME +1 -0
- package/dist/animations.html +31 -0
- package/dist/basic.html +38 -0
- package/dist/baskara.html +31 -0
- package/dist/bezier.html +35 -0
- package/dist/beziersignature.html +29 -0
- package/dist/blackhole.html +28 -0
- package/dist/blob.html +35 -0
- package/dist/coordinates.html +698 -0
- package/dist/cube3d.html +23 -0
- package/dist/demos.css +303 -0
- package/dist/dino.html +42 -0
- package/dist/easing.html +28 -0
- package/dist/events.html +195 -0
- package/dist/fluent.html +647 -0
- package/dist/fluid-simple.html +22 -0
- package/dist/fluid.html +37 -0
- package/dist/fractals.html +36 -0
- package/dist/gameobjects.html +626 -0
- package/dist/gcanvas.es.js +517 -0
- package/dist/gcanvas.es.min.js +1 -1
- package/dist/gcanvas.umd.js +1 -1
- package/dist/gcanvas.umd.min.js +1 -1
- package/dist/genart.html +26 -0
- package/dist/gendream.html +26 -0
- package/dist/group.html +36 -0
- package/dist/home.html +587 -0
- package/dist/hyperbolic001.html +23 -0
- package/dist/hyperbolic002.html +23 -0
- package/dist/hyperbolic003.html +23 -0
- package/dist/hyperbolic004.html +23 -0
- package/dist/hyperbolic005.html +22 -0
- package/dist/index.html +398 -0
- package/dist/isometric.html +34 -0
- package/dist/js/animations.js +452 -0
- package/dist/js/basic.js +204 -0
- package/dist/js/baskara.js +751 -0
- package/dist/js/bezier.js +692 -0
- package/dist/js/beziersignature.js +241 -0
- package/dist/js/blackhole/accretiondisk.obj.js +379 -0
- package/dist/js/blackhole/blackhole.obj.js +318 -0
- package/dist/js/blackhole/index.js +409 -0
- package/dist/js/blackhole/particle.js +56 -0
- package/dist/js/blackhole/starfield.obj.js +218 -0
- package/dist/js/blob.js +2276 -0
- package/dist/js/coordinates.js +840 -0
- package/dist/js/cube3d.js +789 -0
- package/dist/js/dino.js +1420 -0
- package/dist/js/easing.js +477 -0
- package/dist/js/fluent.js +183 -0
- package/dist/js/fluid-simple.js +253 -0
- package/dist/js/fluid.js +527 -0
- package/dist/js/fractals.js +932 -0
- package/dist/js/fractalworker.js +93 -0
- package/dist/js/gameobjects.js +176 -0
- package/dist/js/genart.js +268 -0
- package/dist/js/gendream.js +209 -0
- package/dist/js/group.js +140 -0
- package/dist/js/hyperbolic001.js +310 -0
- package/dist/js/hyperbolic002.js +388 -0
- package/dist/js/hyperbolic003.js +319 -0
- package/dist/js/hyperbolic004.js +345 -0
- package/dist/js/hyperbolic005.js +340 -0
- package/dist/js/info-toggle.js +25 -0
- package/dist/js/isometric.js +863 -0
- package/dist/js/kerr.js +1547 -0
- package/dist/js/lavalamp.js +590 -0
- package/dist/js/layout.js +354 -0
- package/dist/js/mondrian.js +285 -0
- package/dist/js/opacity.js +275 -0
- package/dist/js/painter.js +484 -0
- package/dist/js/particles-showcase.js +514 -0
- package/dist/js/particles.js +299 -0
- package/dist/js/patterns.js +397 -0
- package/dist/js/penrose/artifact.js +69 -0
- package/dist/js/penrose/blackhole.js +121 -0
- package/dist/js/penrose/constants.js +73 -0
- package/dist/js/penrose/game.js +943 -0
- package/dist/js/penrose/lore.js +278 -0
- package/dist/js/penrose/penrosescene.js +892 -0
- package/dist/js/penrose/ship.js +216 -0
- package/dist/js/penrose/sounds.js +211 -0
- package/dist/js/penrose/voidparticle.js +55 -0
- package/dist/js/penrose/voidscene.js +258 -0
- package/dist/js/penrose/voidship.js +144 -0
- package/dist/js/penrose/wormhole.js +46 -0
- package/dist/js/pipeline.js +555 -0
- package/dist/js/plane3d.js +256 -0
- package/dist/js/platformer.js +1579 -0
- package/dist/js/scene.js +304 -0
- package/dist/js/scenes.js +320 -0
- package/dist/js/schrodinger.js +410 -0
- package/dist/js/schwarzschild.js +1015 -0
- package/dist/js/shapes.js +628 -0
- package/dist/js/space/alien.js +171 -0
- package/dist/js/space/boom.js +98 -0
- package/dist/js/space/boss.js +353 -0
- package/dist/js/space/buff.js +73 -0
- package/dist/js/space/bullet.js +102 -0
- package/dist/js/space/constants.js +85 -0
- package/dist/js/space/game.js +1884 -0
- package/dist/js/space/hud.js +112 -0
- package/dist/js/space/laserbeam.js +179 -0
- package/dist/js/space/lightning.js +277 -0
- package/dist/js/space/minion.js +192 -0
- package/dist/js/space/missile.js +212 -0
- package/dist/js/space/player.js +430 -0
- package/dist/js/space/powerup.js +90 -0
- package/dist/js/space/starfield.js +58 -0
- package/dist/js/space/starpower.js +90 -0
- package/dist/js/spacetime.js +559 -0
- package/dist/js/sphere3d.js +229 -0
- package/dist/js/sprite.js +473 -0
- package/dist/js/starfaux/config.js +118 -0
- package/dist/js/starfaux/enemy.js +353 -0
- package/dist/js/starfaux/hud.js +78 -0
- package/dist/js/starfaux/index.js +482 -0
- package/dist/js/starfaux/laser.js +182 -0
- package/dist/js/starfaux/player.js +468 -0
- package/dist/js/starfaux/terrain.js +560 -0
- package/dist/js/study001.js +275 -0
- package/dist/js/study002.js +366 -0
- package/dist/js/study003.js +331 -0
- package/dist/js/study004.js +389 -0
- package/dist/js/study005.js +209 -0
- package/dist/js/study006.js +194 -0
- package/dist/js/study007.js +192 -0
- package/dist/js/study008.js +413 -0
- package/dist/js/svgtween.js +204 -0
- package/dist/js/tde/accretiondisk.js +471 -0
- package/dist/js/tde/blackhole.js +219 -0
- package/dist/js/tde/blackholescene.js +209 -0
- package/dist/js/tde/config.js +59 -0
- package/dist/js/tde/index.js +820 -0
- package/dist/js/tde/jets.js +290 -0
- package/dist/js/tde/lensedstarfield.js +154 -0
- package/dist/js/tde/tdestar.js +297 -0
- package/dist/js/tde/tidalstream.js +372 -0
- package/dist/js/tde_old/blackhole.obj.js +354 -0
- package/dist/js/tde_old/debris.obj.js +791 -0
- package/dist/js/tde_old/flare.obj.js +239 -0
- package/dist/js/tde_old/index.js +448 -0
- package/dist/js/tde_old/star.obj.js +812 -0
- package/dist/js/tetris/config.js +157 -0
- package/dist/js/tetris/grid.js +286 -0
- package/dist/js/tetris/index.js +1195 -0
- package/dist/js/tetris/renderer.js +634 -0
- package/dist/js/tetris/tetrominos.js +280 -0
- package/dist/js/tiles.js +312 -0
- package/dist/js/tweendemo.js +79 -0
- package/dist/js/visibility.js +102 -0
- package/dist/kerr.html +28 -0
- package/dist/lavalamp.html +27 -0
- package/dist/layouts.html +37 -0
- package/dist/logo.svg +4 -0
- package/dist/loop.html +84 -0
- package/dist/mondrian.html +32 -0
- package/dist/og_image.png +0 -0
- package/dist/opacity.html +36 -0
- package/dist/painter.html +39 -0
- package/dist/particles-showcase.html +28 -0
- package/dist/particles.html +24 -0
- package/dist/patterns.html +33 -0
- package/dist/penrose-game.html +31 -0
- package/dist/pipeline.html +737 -0
- package/dist/plane3d.html +24 -0
- package/dist/platformer.html +43 -0
- package/dist/scene.html +33 -0
- package/dist/scenes.html +96 -0
- package/dist/schrodinger.html +27 -0
- package/dist/schwarzschild.html +27 -0
- package/dist/shapes.html +16 -0
- package/dist/space.html +85 -0
- package/dist/spacetime.html +27 -0
- package/dist/sphere3d.html +24 -0
- package/dist/sprite.html +18 -0
- package/dist/starfaux.html +22 -0
- package/dist/study001.html +23 -0
- package/dist/study002.html +23 -0
- package/dist/study003.html +23 -0
- package/dist/study004.html +23 -0
- package/dist/study005.html +22 -0
- package/dist/study006.html +24 -0
- package/dist/study007.html +24 -0
- package/dist/study008.html +22 -0
- package/dist/svgtween.html +29 -0
- package/dist/tde.html +28 -0
- package/dist/tetris3d.html +25 -0
- package/dist/tiles.html +28 -0
- package/dist/transforms.html +400 -0
- package/dist/tween.html +45 -0
- package/dist/visibility.html +33 -0
- package/package.json +1 -1
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlackHole - Event horizon, photon ring, and Hawking radiation
|
|
3
|
+
*
|
|
4
|
+
* Renders the black hole core with gravitational lensing effects,
|
|
5
|
+
* photon ring glow, and Hawking radiation particles.
|
|
6
|
+
*/
|
|
7
|
+
import { GameObject, Easing } from "/gcanvas.es.min.js";
|
|
8
|
+
|
|
9
|
+
// Hawking radiation configuration
|
|
10
|
+
const HAWKING_SPAWN_RATE = 0.15;
|
|
11
|
+
const HAWKING_SPEED = 0.25;
|
|
12
|
+
const HAWKING_LIFETIME = 3.5;
|
|
13
|
+
|
|
14
|
+
export class BlackHole extends GameObject {
|
|
15
|
+
/**
|
|
16
|
+
* @param {Game} game - Game instance
|
|
17
|
+
* @param {Object} options
|
|
18
|
+
* @param {Camera3D} options.camera - Camera for projection
|
|
19
|
+
* @param {StateMachine} options.formationFSM - Formation state machine
|
|
20
|
+
* @param {number} options.baseScale - Base scale for sizing
|
|
21
|
+
* @param {number} options.bhRadius - Black hole radius
|
|
22
|
+
* @param {number} options.diskOuter - Outer disk radius (for Hawking bounds)
|
|
23
|
+
*/
|
|
24
|
+
constructor(game, options = {}) {
|
|
25
|
+
super(game, options);
|
|
26
|
+
|
|
27
|
+
this.camera = options.camera;
|
|
28
|
+
this.formationFSM = options.formationFSM;
|
|
29
|
+
|
|
30
|
+
// Sizing
|
|
31
|
+
this.baseScale = options.baseScale ?? 500;
|
|
32
|
+
this.bhRadius = options.bhRadius ?? 40;
|
|
33
|
+
this.diskOuter = options.diskOuter ?? 175;
|
|
34
|
+
|
|
35
|
+
// Hawking radiation
|
|
36
|
+
this.hawkingParticles = [];
|
|
37
|
+
this.hawkingSpawnTimer = 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Update sizing when window resizes.
|
|
42
|
+
*/
|
|
43
|
+
updateSizing(baseScale, bhRadius, diskOuter) {
|
|
44
|
+
this.baseScale = baseScale;
|
|
45
|
+
this.bhRadius = bhRadius;
|
|
46
|
+
this.diskOuter = diskOuter;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Reset Hawking radiation state.
|
|
51
|
+
*/
|
|
52
|
+
reset() {
|
|
53
|
+
this.hawkingParticles = [];
|
|
54
|
+
this.hawkingSpawnTimer = 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Calculate formation progress (0-1) based on consumed particles.
|
|
59
|
+
*/
|
|
60
|
+
getFormationLambda(particlesConsumed, totalParticleMass) {
|
|
61
|
+
const state = this.formationFSM.state;
|
|
62
|
+
const progress = this.formationFSM.progress;
|
|
63
|
+
|
|
64
|
+
if (state === "infall") {
|
|
65
|
+
if (!particlesConsumed || particlesConsumed === 0) return 0;
|
|
66
|
+
const maxConsumed = totalParticleMass * 0.4;
|
|
67
|
+
const consumedRatio = Math.min(1, particlesConsumed / maxConsumed);
|
|
68
|
+
return consumedRatio * 0.5;
|
|
69
|
+
} else if (state === "collapse") {
|
|
70
|
+
const delayedProgress = Math.max(0, (progress - 0.2) / 0.8);
|
|
71
|
+
return 0.5 + Easing.easeOutQuad(delayedProgress) * 0.3;
|
|
72
|
+
} else if (state === "circularize") {
|
|
73
|
+
return 0.8 + progress * 0.2;
|
|
74
|
+
}
|
|
75
|
+
return 1; // stable
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Spawn a new Hawking radiation particle.
|
|
80
|
+
*/
|
|
81
|
+
spawnHawkingParticle() {
|
|
82
|
+
const angle = Math.random() * Math.PI * 2;
|
|
83
|
+
const startRadius = this.bhRadius * 1.05;
|
|
84
|
+
|
|
85
|
+
// ~10% chance to be an "escaper"
|
|
86
|
+
const isEscaper = Math.random() < 0.1;
|
|
87
|
+
|
|
88
|
+
this.hawkingParticles.push({
|
|
89
|
+
angle,
|
|
90
|
+
radius: startRadius,
|
|
91
|
+
speed: isEscaper
|
|
92
|
+
? HAWKING_SPEED * (1.5 + Math.random() * 1.0)
|
|
93
|
+
: HAWKING_SPEED * (0.6 + Math.random() * 0.8),
|
|
94
|
+
size: isEscaper ? 3 + Math.random() * 2 : 2 + Math.random() * 2,
|
|
95
|
+
brightness: isEscaper ? 1.0 : 0.8 + Math.random() * 0.2,
|
|
96
|
+
age: 0,
|
|
97
|
+
wobblePhase: Math.random() * Math.PI * 2,
|
|
98
|
+
wobbleSpeed: 2 + Math.random() * 3,
|
|
99
|
+
isEscaper,
|
|
100
|
+
maxRadius: isEscaper ? this.baseScale * 0.8 : this.diskOuter,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
update(dt) {
|
|
105
|
+
super.update(dt);
|
|
106
|
+
this.updateHawkingRadiation(dt);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Update Hawking radiation particles.
|
|
111
|
+
*/
|
|
112
|
+
updateHawkingRadiation(dt) {
|
|
113
|
+
// Only spawn when stable
|
|
114
|
+
if (this.formationFSM.is("stable")) {
|
|
115
|
+
this.hawkingSpawnTimer += dt;
|
|
116
|
+
if (this.hawkingSpawnTimer > 1 / HAWKING_SPAWN_RATE) {
|
|
117
|
+
this.spawnHawkingParticle();
|
|
118
|
+
this.hawkingSpawnTimer = 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Update existing particles
|
|
123
|
+
for (let i = this.hawkingParticles.length - 1; i >= 0; i--) {
|
|
124
|
+
const p = this.hawkingParticles[i];
|
|
125
|
+
p.age += dt;
|
|
126
|
+
p.radius += p.speed * this.bhRadius * dt;
|
|
127
|
+
const wobble = Math.sin(p.age * p.wobbleSpeed + p.wobblePhase) * 0.02;
|
|
128
|
+
p.angle += (0.05 + wobble) * dt;
|
|
129
|
+
|
|
130
|
+
const maxLife = p.isEscaper ? HAWKING_LIFETIME * 2.5 : HAWKING_LIFETIME;
|
|
131
|
+
if (p.age > maxLife || p.radius > p.maxRadius) {
|
|
132
|
+
this.hawkingParticles.splice(i, 1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Build render list with black hole and Hawking particles.
|
|
139
|
+
*/
|
|
140
|
+
buildRenderList(lambda) {
|
|
141
|
+
const renderList = [];
|
|
142
|
+
|
|
143
|
+
// Black hole (only render after formation starts)
|
|
144
|
+
if (lambda > 0.05) {
|
|
145
|
+
const holeProj = this.camera.project(0, 0, 0);
|
|
146
|
+
renderList.push({
|
|
147
|
+
type: "hole",
|
|
148
|
+
z: holeProj.z,
|
|
149
|
+
x: holeProj.x,
|
|
150
|
+
y: holeProj.y,
|
|
151
|
+
scale: holeProj.scale,
|
|
152
|
+
lambda: lambda,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Hawking particles
|
|
157
|
+
for (const p of this.hawkingParticles) {
|
|
158
|
+
const x = Math.cos(p.angle) * p.radius;
|
|
159
|
+
const z = Math.sin(p.angle) * p.radius;
|
|
160
|
+
const y = 0;
|
|
161
|
+
|
|
162
|
+
const cosY = Math.cos(this.camera.rotationY);
|
|
163
|
+
const sinY = Math.sin(this.camera.rotationY);
|
|
164
|
+
let xCam = x * cosY - z * sinY;
|
|
165
|
+
let zCam = x * sinY + z * cosY;
|
|
166
|
+
|
|
167
|
+
const cosX = Math.cos(this.camera.rotationX);
|
|
168
|
+
const sinX = Math.sin(this.camera.rotationX);
|
|
169
|
+
let yCam = y * cosX - zCam * sinX;
|
|
170
|
+
zCam = y * sinX + zCam * cosX;
|
|
171
|
+
|
|
172
|
+
const perspectiveScale =
|
|
173
|
+
this.camera.perspective / (this.camera.perspective + zCam);
|
|
174
|
+
const screenX = xCam * perspectiveScale;
|
|
175
|
+
const screenY = yCam * perspectiveScale;
|
|
176
|
+
|
|
177
|
+
if (zCam < -this.camera.perspective + 10) continue;
|
|
178
|
+
|
|
179
|
+
const maxLife = p.isEscaper ? HAWKING_LIFETIME * 2.5 : HAWKING_LIFETIME;
|
|
180
|
+
const ageRatio = p.age / maxLife;
|
|
181
|
+
const fadeIn = Math.min(1, p.age * 4);
|
|
182
|
+
const fadeOut = 1 - Math.pow(ageRatio, 2);
|
|
183
|
+
const brightness = p.brightness * fadeIn * fadeOut;
|
|
184
|
+
|
|
185
|
+
renderList.push({
|
|
186
|
+
type: "hawking",
|
|
187
|
+
z: zCam,
|
|
188
|
+
x: screenX,
|
|
189
|
+
y: screenY,
|
|
190
|
+
scale: perspectiveScale,
|
|
191
|
+
size: p.size,
|
|
192
|
+
brightness: brightness,
|
|
193
|
+
age: p.age,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return renderList;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Draw the event horizon and photon ring.
|
|
202
|
+
* Static method for use in main render loop.
|
|
203
|
+
*/
|
|
204
|
+
static drawHole(ctx, item, bhRadius) {
|
|
205
|
+
const r = bhRadius * item.scale * item.lambda;
|
|
206
|
+
|
|
207
|
+
// Photon ring glow (additive)
|
|
208
|
+
ctx.globalCompositeOperation = "screen";
|
|
209
|
+
const glowIntensity = item.lambda;
|
|
210
|
+
const gradient = ctx.createRadialGradient(
|
|
211
|
+
item.x,
|
|
212
|
+
item.y,
|
|
213
|
+
r * 0.8,
|
|
214
|
+
item.x,
|
|
215
|
+
item.y,
|
|
216
|
+
r * 1.5,
|
|
217
|
+
);
|
|
218
|
+
gradient.addColorStop(0, `rgba(255, 200, 100, ${glowIntensity})`);
|
|
219
|
+
gradient.addColorStop(0.2, `rgba(255, 150, 50, ${0.6 * glowIntensity})`);
|
|
220
|
+
gradient.addColorStop(1, "rgba(255, 50, 0, 0)");
|
|
221
|
+
|
|
222
|
+
ctx.fillStyle = gradient;
|
|
223
|
+
ctx.beginPath();
|
|
224
|
+
ctx.arc(item.x, item.y, r * 1.5, 0, Math.PI * 2);
|
|
225
|
+
ctx.fill();
|
|
226
|
+
ctx.globalCompositeOperation = "source-over";
|
|
227
|
+
|
|
228
|
+
// Event horizon (void)
|
|
229
|
+
ctx.beginPath();
|
|
230
|
+
ctx.arc(item.x, item.y, r, 0, Math.PI * 2);
|
|
231
|
+
ctx.fillStyle = "#000";
|
|
232
|
+
ctx.fill();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Draw a Hawking radiation particle.
|
|
237
|
+
* Static method for use in main render loop.
|
|
238
|
+
*/
|
|
239
|
+
static drawHawkingParticle(ctx, item, baseScale) {
|
|
240
|
+
const size = item.size * item.scale * (baseScale * 0.001);
|
|
241
|
+
if (size < 0.1) return;
|
|
242
|
+
|
|
243
|
+
const pulseIntensity = 0.8 + 0.2 * Math.sin(item.age * 8);
|
|
244
|
+
|
|
245
|
+
// Outer glow
|
|
246
|
+
ctx.globalCompositeOperation = "screen";
|
|
247
|
+
ctx.shadowColor = "rgba(0, 255, 200, 0.9)";
|
|
248
|
+
ctx.shadowBlur = 25 * item.brightness;
|
|
249
|
+
|
|
250
|
+
// Core - cyan-green
|
|
251
|
+
ctx.fillStyle = `rgba(100, 255, 220, ${item.brightness * 0.9 * pulseIntensity})`;
|
|
252
|
+
ctx.beginPath();
|
|
253
|
+
ctx.arc(item.x, item.y, size, 0, Math.PI * 2);
|
|
254
|
+
ctx.fill();
|
|
255
|
+
|
|
256
|
+
// Inner bright core
|
|
257
|
+
ctx.fillStyle = `rgba(200, 255, 250, ${item.brightness * 0.7})`;
|
|
258
|
+
ctx.beginPath();
|
|
259
|
+
ctx.arc(item.x, item.y, size * 0.4, 0, Math.PI * 2);
|
|
260
|
+
ctx.fill();
|
|
261
|
+
|
|
262
|
+
ctx.shadowBlur = 0;
|
|
263
|
+
ctx.globalCompositeOperation = "source-over";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Draw the formation collapse flash.
|
|
268
|
+
*/
|
|
269
|
+
drawFormationFlash(ctx, cx, cy, lambda, time) {
|
|
270
|
+
const intensity = 1 - lambda / 0.3;
|
|
271
|
+
|
|
272
|
+
// Collapse flash - bright white center
|
|
273
|
+
const size = this.bhRadius * (1 - lambda) * 2;
|
|
274
|
+
const gradient = ctx.createRadialGradient(cx, cy, 0, cx, cy, size);
|
|
275
|
+
gradient.addColorStop(0, `rgba(255, 255, 255, ${0.9 * intensity})`);
|
|
276
|
+
gradient.addColorStop(0.3, `rgba(255, 220, 180, ${0.6 * intensity})`);
|
|
277
|
+
gradient.addColorStop(0.6, `rgba(255, 150, 100, ${0.3 * intensity})`);
|
|
278
|
+
gradient.addColorStop(1, "transparent");
|
|
279
|
+
|
|
280
|
+
ctx.fillStyle = gradient;
|
|
281
|
+
ctx.beginPath();
|
|
282
|
+
ctx.arc(cx, cy, size, 0, Math.PI * 2);
|
|
283
|
+
ctx.fill();
|
|
284
|
+
|
|
285
|
+
// Infalling streaks
|
|
286
|
+
if (lambda > 0.1) {
|
|
287
|
+
const streakAlpha = intensity * 0.6;
|
|
288
|
+
for (let i = 0; i < 8; i++) {
|
|
289
|
+
const angle = (i / 8) * Math.PI * 2 + time * 0.5;
|
|
290
|
+
const startR = this.bhRadius * 2;
|
|
291
|
+
const endR = this.bhRadius * lambda * 0.5;
|
|
292
|
+
|
|
293
|
+
const streakGradient = ctx.createLinearGradient(
|
|
294
|
+
cx + Math.cos(angle) * startR,
|
|
295
|
+
cy + Math.sin(angle) * startR,
|
|
296
|
+
cx + Math.cos(angle) * endR,
|
|
297
|
+
cy + Math.sin(angle) * endR,
|
|
298
|
+
);
|
|
299
|
+
streakGradient.addColorStop(0, "transparent");
|
|
300
|
+
streakGradient.addColorStop(
|
|
301
|
+
0.5,
|
|
302
|
+
`rgba(255, 200, 150, ${streakAlpha * 0.5})`,
|
|
303
|
+
);
|
|
304
|
+
streakGradient.addColorStop(1, `rgba(255, 255, 200, ${streakAlpha})`);
|
|
305
|
+
|
|
306
|
+
ctx.strokeStyle = streakGradient;
|
|
307
|
+
ctx.lineWidth = 2;
|
|
308
|
+
ctx.beginPath();
|
|
309
|
+
ctx.moveTo(
|
|
310
|
+
cx + Math.cos(angle) * startR,
|
|
311
|
+
cy + Math.sin(angle) * startR,
|
|
312
|
+
);
|
|
313
|
+
ctx.lineTo(cx + Math.cos(angle) * endR, cy + Math.sin(angle) * endR);
|
|
314
|
+
ctx.stroke();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|