Spaces:
Running
Running
Update stars.js
Browse files
stars.js
CHANGED
@@ -1,32 +1,33 @@
|
|
1 |
-
//
|
2 |
|
3 |
(() => {
|
4 |
const canvas = document.getElementById("starfield");
|
5 |
const ctx = canvas.getContext("2d");
|
6 |
let width, height;
|
7 |
-
let bgCanvas, bgCtx
|
8 |
let starSprites = [];
|
9 |
let layers = [];
|
10 |
let comets = [];
|
11 |
let pulses = [];
|
12 |
|
13 |
-
//
|
14 |
const layerConfigs = [
|
15 |
-
{ count:
|
16 |
-
{ count:
|
17 |
-
{ count:
|
|
|
18 |
];
|
19 |
|
20 |
const rand = (min, max) => min + Math.random() * (max - min);
|
21 |
|
22 |
-
// Handle resize: update sizes, cache background
|
23 |
function resize() {
|
24 |
width = window.innerWidth;
|
25 |
height = window.innerHeight;
|
26 |
canvas.width = width;
|
27 |
canvas.height = height;
|
28 |
|
29 |
-
// Cache a deep
|
30 |
bgCanvas = document.createElement("canvas");
|
31 |
bgCanvas.width = width;
|
32 |
bgCanvas.height = height;
|
@@ -35,56 +36,31 @@
|
|
35 |
width / 2, height / 2, 0,
|
36 |
width / 2, height / 2, width
|
37 |
);
|
38 |
-
bgGrad.addColorStop(0, "#
|
39 |
bgGrad.addColorStop(1, "#000000");
|
40 |
bgCtx.fillStyle = bgGrad;
|
41 |
bgCtx.fillRect(0, 0, width, height);
|
42 |
-
|
43 |
-
// Pre-render a static nebula field
|
44 |
-
nebulaCanvas = document.createElement("canvas");
|
45 |
-
nebulaCanvas.width = width;
|
46 |
-
nebulaCanvas.height = height;
|
47 |
-
nebulaCtx = nebulaCanvas.getContext("2d");
|
48 |
-
|
49 |
-
// Three colored cloud blobs
|
50 |
-
[
|
51 |
-
["#550066", "#220022"],
|
52 |
-
["#006655", "#002222"],
|
53 |
-
["#665500", "#332200"],
|
54 |
-
].forEach(([c1, c2]) => {
|
55 |
-
const x = rand(0, width);
|
56 |
-
const y = rand(0, height);
|
57 |
-
const r = rand(width * 0.3, width * 0.7);
|
58 |
-
const nebGrad = nebulaCtx.createRadialGradient(x, y, 0, x, y, r);
|
59 |
-
nebGrad.addColorStop(0, `${c1}33`);
|
60 |
-
nebGrad.addColorStop(1, `${c2}00`);
|
61 |
-
nebulaCtx.fillStyle = nebGrad;
|
62 |
-
nebulaCtx.beginPath();
|
63 |
-
nebulaCtx.arc(x, y, r, 0, Math.PI * 2);
|
64 |
-
nebulaCtx.fill();
|
65 |
-
});
|
66 |
}
|
67 |
|
68 |
-
// Pre-render star sprites with glow
|
69 |
function createStarSprites() {
|
70 |
-
starSprites =
|
71 |
-
layerConfigs.forEach(cfg => {
|
72 |
const r = cfg.size;
|
73 |
-
const off = document.createElement("canvas");
|
74 |
const sz = r * 6;
|
|
|
75 |
off.width = off.height = sz;
|
76 |
const octx = off.getContext("2d");
|
77 |
octx.fillStyle = "#fff";
|
78 |
octx.shadowColor = "#fff";
|
79 |
-
octx.shadowBlur = r *
|
80 |
octx.beginPath();
|
81 |
octx.arc(sz / 2, sz / 2, r, 0, Math.PI * 2);
|
82 |
octx.fill();
|
83 |
-
|
84 |
});
|
85 |
}
|
86 |
|
87 |
-
// Initialize star
|
88 |
function initStars() {
|
89 |
layers = layerConfigs.map((cfg, idx) => {
|
90 |
const stars = [];
|
@@ -93,7 +69,7 @@
|
|
93 |
x: rand(-width / 2, width / 2),
|
94 |
y: rand(-height / 2, height / 2),
|
95 |
z: rand(1, width),
|
96 |
-
|
97 |
sprIndex: idx,
|
98 |
});
|
99 |
}
|
@@ -106,34 +82,34 @@
|
|
106 |
if (Math.random() < 0.002) {
|
107 |
comets.push({
|
108 |
x: rand(0, width),
|
109 |
-
y: rand(0, height * 0.
|
110 |
len: 0,
|
111 |
-
maxLen: rand(
|
112 |
angle: Math.PI * 0.25 + rand(-0.3, 0.3),
|
113 |
speed: rand(10, 16),
|
114 |
});
|
115 |
}
|
116 |
}
|
117 |
|
118 |
-
//
|
119 |
canvas.addEventListener("click", e => {
|
120 |
-
pulses.push({ x: e.clientX, y: e.clientY, r: 0, alpha: 0.
|
121 |
});
|
122 |
|
123 |
-
// Update positions,
|
124 |
function update() {
|
125 |
const now = Date.now();
|
126 |
layers.forEach(layer => {
|
127 |
layer.stars.forEach(s => {
|
128 |
-
s.z -= layer.speed;
|
129 |
if (s.z <= 0) {
|
130 |
s.z = width;
|
131 |
s.x = rand(-width / 2, width / 2);
|
132 |
s.y = rand(-height / 2, height / 2);
|
133 |
-
s.
|
134 |
}
|
135 |
-
//
|
136 |
-
s.
|
137 |
});
|
138 |
});
|
139 |
|
@@ -146,8 +122,8 @@
|
|
146 |
|
147 |
pulses = pulses.filter(p => p.alpha > 0);
|
148 |
pulses.forEach(p => {
|
149 |
-
p.r +=
|
150 |
-
p.alpha -= 0.
|
151 |
});
|
152 |
|
153 |
maybeComet();
|
@@ -155,46 +131,36 @@
|
|
155 |
|
156 |
// Draw the full scene
|
157 |
function draw() {
|
158 |
-
// 1)
|
159 |
ctx.clearRect(0, 0, width, height);
|
160 |
ctx.drawImage(bgCanvas, 0, 0);
|
161 |
|
162 |
-
// 2)
|
163 |
-
|
164 |
-
ctx.save();
|
165 |
-
ctx.translate(width / 2, height / 2);
|
166 |
-
ctx.rotate(t);
|
167 |
-
ctx.globalAlpha = 0.1;
|
168 |
-
ctx.globalCompositeOperation = "screen";
|
169 |
-
ctx.drawImage(nebulaCanvas, -width / 2, -height / 2);
|
170 |
-
ctx.restore();
|
171 |
-
ctx.globalCompositeOperation = "source-over";
|
172 |
-
ctx.globalAlpha = 1;
|
173 |
-
|
174 |
-
// 3) Motion trails
|
175 |
-
ctx.fillStyle = "rgba(0,0,0,0.2)";
|
176 |
ctx.fillRect(0, 0, width, height);
|
177 |
|
178 |
-
//
|
179 |
layers.forEach((layer, li) => {
|
180 |
-
const
|
181 |
ctx.globalCompositeOperation = "lighter";
|
182 |
layer.stars.forEach(s => {
|
183 |
const k = (width / 2) / s.z;
|
184 |
const x = s.x * k + width / 2;
|
185 |
const y = s.y * k + height / 2;
|
186 |
if (x < 0 || x > width || y < 0 || y > height) return;
|
187 |
-
const ds = (1 - s.z / width) * layer.size;
|
188 |
-
|
189 |
-
|
|
|
|
|
190 |
});
|
191 |
ctx.globalCompositeOperation = "source-over";
|
192 |
});
|
193 |
ctx.globalAlpha = 1;
|
194 |
|
195 |
-
//
|
196 |
comets.forEach(c => {
|
197 |
-
ctx.strokeStyle = "rgba(255,255,255,0.
|
198 |
ctx.lineWidth = 1.2;
|
199 |
ctx.beginPath();
|
200 |
ctx.moveTo(c.x, c.y);
|
@@ -205,7 +171,7 @@
|
|
205 |
ctx.stroke();
|
206 |
});
|
207 |
|
208 |
-
//
|
209 |
pulses.forEach(p => {
|
210 |
const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.r);
|
211 |
grd.addColorStop(0, `rgba(255,255,255,${p.alpha})`);
|
@@ -213,17 +179,16 @@
|
|
213 |
ctx.fillStyle = grd;
|
214 |
ctx.fillRect(0, 0, width, height);
|
215 |
});
|
216 |
-
ctx.globalAlpha = 1;
|
217 |
}
|
218 |
|
219 |
-
//
|
220 |
function loop() {
|
221 |
update();
|
222 |
draw();
|
223 |
requestAnimationFrame(loop);
|
224 |
}
|
225 |
|
226 |
-
//
|
227 |
function init() {
|
228 |
resize();
|
229 |
createStarSprites();
|
|
|
1 |
+
// High-Contrast, High-Density Starfield – Script Only
|
2 |
|
3 |
(() => {
|
4 |
const canvas = document.getElementById("starfield");
|
5 |
const ctx = canvas.getContext("2d");
|
6 |
let width, height;
|
7 |
+
let bgCanvas, bgCtx;
|
8 |
let starSprites = [];
|
9 |
let layers = [];
|
10 |
let comets = [];
|
11 |
let pulses = [];
|
12 |
|
13 |
+
// Four parallax star layers: count, base speed, draw size
|
14 |
const layerConfigs = [
|
15 |
+
{ count: 400, speed: 1.5, size: 2.5 },
|
16 |
+
{ count: 300, speed: 3.0, size: 4.0 },
|
17 |
+
{ count: 200, speed: 6.0, size: 5.5 },
|
18 |
+
{ count: 100, speed: 10.0, size: 8.0 },
|
19 |
];
|
20 |
|
21 |
const rand = (min, max) => min + Math.random() * (max - min);
|
22 |
|
23 |
+
// Handle resize: update sizes, cache background
|
24 |
function resize() {
|
25 |
width = window.innerWidth;
|
26 |
height = window.innerHeight;
|
27 |
canvas.width = width;
|
28 |
canvas.height = height;
|
29 |
|
30 |
+
// Cache a deep-space gradient background
|
31 |
bgCanvas = document.createElement("canvas");
|
32 |
bgCanvas.width = width;
|
33 |
bgCanvas.height = height;
|
|
|
36 |
width / 2, height / 2, 0,
|
37 |
width / 2, height / 2, width
|
38 |
);
|
39 |
+
bgGrad.addColorStop(0, "#111122");
|
40 |
bgGrad.addColorStop(1, "#000000");
|
41 |
bgCtx.fillStyle = bgGrad;
|
42 |
bgCtx.fillRect(0, 0, width, height);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
}
|
44 |
|
45 |
+
// Pre-render star sprites with strong glow
|
46 |
function createStarSprites() {
|
47 |
+
starSprites = layerConfigs.map(cfg => {
|
|
|
48 |
const r = cfg.size;
|
|
|
49 |
const sz = r * 6;
|
50 |
+
const off = document.createElement("canvas");
|
51 |
off.width = off.height = sz;
|
52 |
const octx = off.getContext("2d");
|
53 |
octx.fillStyle = "#fff";
|
54 |
octx.shadowColor = "#fff";
|
55 |
+
octx.shadowBlur = r * 3;
|
56 |
octx.beginPath();
|
57 |
octx.arc(sz / 2, sz / 2, r, 0, Math.PI * 2);
|
58 |
octx.fill();
|
59 |
+
return off;
|
60 |
});
|
61 |
}
|
62 |
|
63 |
+
// Initialize each star layer
|
64 |
function initStars() {
|
65 |
layers = layerConfigs.map((cfg, idx) => {
|
66 |
const stars = [];
|
|
|
69 |
x: rand(-width / 2, width / 2),
|
70 |
y: rand(-height / 2, height / 2),
|
71 |
z: rand(1, width),
|
72 |
+
twAngle: rand(0, Math.PI * 2),
|
73 |
sprIndex: idx,
|
74 |
});
|
75 |
}
|
|
|
82 |
if (Math.random() < 0.002) {
|
83 |
comets.push({
|
84 |
x: rand(0, width),
|
85 |
+
y: rand(0, height * 0.2),
|
86 |
len: 0,
|
87 |
+
maxLen: rand(80, 200),
|
88 |
angle: Math.PI * 0.25 + rand(-0.3, 0.3),
|
89 |
speed: rand(10, 16),
|
90 |
});
|
91 |
}
|
92 |
}
|
93 |
|
94 |
+
// Click for warp pulse
|
95 |
canvas.addEventListener("click", e => {
|
96 |
+
pulses.push({ x: e.clientX, y: e.clientY, r: 0, alpha: 0.6 });
|
97 |
});
|
98 |
|
99 |
+
// Update star positions, twinkles, comets, pulses
|
100 |
function update() {
|
101 |
const now = Date.now();
|
102 |
layers.forEach(layer => {
|
103 |
layer.stars.forEach(s => {
|
104 |
+
s.z -= layer.speed; // faster movement
|
105 |
if (s.z <= 0) {
|
106 |
s.z = width;
|
107 |
s.x = rand(-width / 2, width / 2);
|
108 |
s.y = rand(-height / 2, height / 2);
|
109 |
+
s.twAngle = rand(0, Math.PI * 2);
|
110 |
}
|
111 |
+
// sinusoidal twinkle based on depth + time
|
112 |
+
s.twAngle += 0.02;
|
113 |
});
|
114 |
});
|
115 |
|
|
|
122 |
|
123 |
pulses = pulses.filter(p => p.alpha > 0);
|
124 |
pulses.forEach(p => {
|
125 |
+
p.r += 30;
|
126 |
+
p.alpha -= 0.012;
|
127 |
});
|
128 |
|
129 |
maybeComet();
|
|
|
131 |
|
132 |
// Draw the full scene
|
133 |
function draw() {
|
134 |
+
// 1) Background
|
135 |
ctx.clearRect(0, 0, width, height);
|
136 |
ctx.drawImage(bgCanvas, 0, 0);
|
137 |
|
138 |
+
// 2) Motion trails
|
139 |
+
ctx.fillStyle = "rgba(0,0,0,0.15)";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
ctx.fillRect(0, 0, width, height);
|
141 |
|
142 |
+
// 3) Stars
|
143 |
layers.forEach((layer, li) => {
|
144 |
+
const sprite = starSprites[li];
|
145 |
ctx.globalCompositeOperation = "lighter";
|
146 |
layer.stars.forEach(s => {
|
147 |
const k = (width / 2) / s.z;
|
148 |
const x = s.x * k + width / 2;
|
149 |
const y = s.y * k + height / 2;
|
150 |
if (x < 0 || x > width || y < 0 || y > height) return;
|
151 |
+
const ds = (1 - s.z / width) * layer.size * 1.5;
|
152 |
+
// compute twinkle alpha
|
153 |
+
const alpha = 0.5 + 0.5 * Math.sin(s.twAngle + s.z / 30);
|
154 |
+
ctx.globalAlpha = alpha;
|
155 |
+
ctx.drawImage(sprite, x - ds, y - ds, ds * 2, ds * 2);
|
156 |
});
|
157 |
ctx.globalCompositeOperation = "source-over";
|
158 |
});
|
159 |
ctx.globalAlpha = 1;
|
160 |
|
161 |
+
// 4) Comets
|
162 |
comets.forEach(c => {
|
163 |
+
ctx.strokeStyle = "rgba(255,255,255,0.8)";
|
164 |
ctx.lineWidth = 1.2;
|
165 |
ctx.beginPath();
|
166 |
ctx.moveTo(c.x, c.y);
|
|
|
171 |
ctx.stroke();
|
172 |
});
|
173 |
|
174 |
+
// 5) Warp pulses
|
175 |
pulses.forEach(p => {
|
176 |
const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.r);
|
177 |
grd.addColorStop(0, `rgba(255,255,255,${p.alpha})`);
|
|
|
179 |
ctx.fillStyle = grd;
|
180 |
ctx.fillRect(0, 0, width, height);
|
181 |
});
|
|
|
182 |
}
|
183 |
|
184 |
+
// Animation loop
|
185 |
function loop() {
|
186 |
update();
|
187 |
draw();
|
188 |
requestAnimationFrame(loop);
|
189 |
}
|
190 |
|
191 |
+
// Initialize everything
|
192 |
function init() {
|
193 |
resize();
|
194 |
createStarSprites();
|