Spaces:
Running
Running
Update stars.js
Browse files
stars.js
CHANGED
@@ -1,96 +1,115 @@
|
|
1 |
-
//
|
2 |
|
3 |
(() => {
|
4 |
const canvas = document.getElementById("starfield");
|
5 |
const ctx = canvas.getContext("2d");
|
6 |
-
let
|
7 |
-
let
|
8 |
-
let
|
9 |
-
let starLayers = [];
|
10 |
let comets = [];
|
11 |
-
let
|
12 |
|
13 |
-
//
|
14 |
-
const
|
15 |
-
{ count:
|
16 |
-
{ count:
|
17 |
-
{ count:
|
18 |
];
|
19 |
|
20 |
// Utility
|
21 |
const rand = (min, max) => min + Math.random() * (max - min);
|
22 |
|
23 |
-
// Resize &
|
24 |
function resize() {
|
25 |
-
dpr = window.devicePixelRatio || 1;
|
26 |
width = window.innerWidth;
|
27 |
height = window.innerHeight;
|
28 |
-
canvas.width = width
|
29 |
-
canvas.height = height
|
30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
}
|
32 |
|
33 |
// Initialize star layers
|
34 |
function initStars() {
|
35 |
-
|
36 |
const stars = [];
|
37 |
for (let i = 0; i < cfg.count; i++) {
|
38 |
stars.push({
|
39 |
x: rand(-width / 2, width / 2),
|
40 |
y: rand(-height / 2, height / 2),
|
41 |
z: rand(1, width),
|
42 |
-
|
43 |
-
|
44 |
});
|
45 |
}
|
46 |
return { ...cfg, stars };
|
47 |
});
|
48 |
}
|
49 |
|
50 |
-
//
|
51 |
function maybeComet() {
|
52 |
-
if (Math.random() < 0.
|
53 |
comets.push({
|
54 |
x: rand(0, width),
|
55 |
y: rand(0, height * 0.2),
|
56 |
len: 0,
|
57 |
-
maxLen: rand(
|
58 |
angle: Math.PI * 0.25 + rand(-0.2, 0.2),
|
59 |
-
speed: rand(
|
60 |
});
|
61 |
}
|
62 |
}
|
63 |
|
64 |
-
//
|
65 |
-
canvas.addEventListener("click", e => {
|
66 |
-
pulses.push({
|
67 |
-
x: e.clientX,
|
68 |
-
y: e.clientY,
|
69 |
-
r: 0,
|
70 |
-
maxR: Math.hypot(width, height),
|
71 |
-
alpha: 0.6
|
72 |
-
});
|
73 |
-
});
|
74 |
-
|
75 |
-
// Mouse-based speed control
|
76 |
window.addEventListener("mousemove", e => {
|
77 |
-
speedFactor = 0.5 + (e.clientX / width) * 2.
|
78 |
});
|
79 |
|
80 |
-
// Update positions
|
81 |
function update() {
|
82 |
-
|
|
|
83 |
layer.stars.forEach(s => {
|
84 |
-
s.z -=
|
85 |
if (s.z <= 0) {
|
86 |
s.z = width;
|
87 |
s.x = rand(-width / 2, width / 2);
|
88 |
s.y = rand(-height / 2, height / 2);
|
89 |
-
s.
|
90 |
-
s.hue = rand(200, 260);
|
91 |
}
|
92 |
-
// twinkle
|
93 |
-
s.a
|
|
|
94 |
});
|
95 |
});
|
96 |
|
@@ -101,61 +120,46 @@
|
|
101 |
c.y += Math.sin(c.angle) * c.speed;
|
102 |
});
|
103 |
|
104 |
-
pulses = pulses.filter(p => p.alpha > 0);
|
105 |
-
pulses.forEach(p => {
|
106 |
-
p.r += 30;
|
107 |
-
p.alpha -= 0.008;
|
108 |
-
});
|
109 |
-
|
110 |
maybeComet();
|
111 |
}
|
112 |
|
113 |
-
// Draw
|
114 |
function draw() {
|
115 |
-
// 1)
|
116 |
-
ctx.
|
117 |
-
ctx.fillRect(0, 0, width, height);
|
118 |
|
119 |
-
// 2)
|
120 |
-
|
121 |
-
width / 2, height / 2, width / 3,
|
122 |
-
width / 2, height / 2, width / 1.2
|
123 |
-
);
|
124 |
-
vig.addColorStop(0, "rgba(0,0,0,0)");
|
125 |
-
vig.addColorStop(1, "rgba(0,0,0,0.7)");
|
126 |
-
ctx.fillStyle = vig;
|
127 |
ctx.fillRect(0, 0, width, height);
|
128 |
|
129 |
-
// 3) stars
|
130 |
-
|
131 |
-
|
132 |
-
|
|
|
|
|
133 |
const k = (width / 2) / s.z;
|
134 |
const x = s.x * k + width / 2;
|
135 |
const y = s.y * k + height / 2;
|
136 |
if (x < 0 || x > width || y < 0 || y > height) return;
|
137 |
-
|
138 |
-
|
139 |
-
ctx.
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
ctx.fill();
|
147 |
-
ctx.restore();
|
148 |
});
|
149 |
});
|
150 |
ctx.globalCompositeOperation = "source-over";
|
|
|
151 |
|
152 |
-
// 4) comets
|
153 |
comets.forEach(c => {
|
154 |
-
ctx.
|
155 |
-
ctx.
|
156 |
-
ctx.lineWidth = 2;
|
157 |
-
ctx.shadowColor = "white";
|
158 |
-
ctx.shadowBlur = 8;
|
159 |
ctx.beginPath();
|
160 |
ctx.moveTo(c.x, c.y);
|
161 |
ctx.lineTo(
|
@@ -163,18 +167,6 @@
|
|
163 |
c.y - Math.sin(c.angle) * c.len
|
164 |
);
|
165 |
ctx.stroke();
|
166 |
-
ctx.restore();
|
167 |
-
});
|
168 |
-
|
169 |
-
// 5) warp pulses
|
170 |
-
pulses.forEach(p => {
|
171 |
-
ctx.save();
|
172 |
-
const grd = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.r);
|
173 |
-
grd.addColorStop(0, `rgba(255,255,255,${p.alpha})`);
|
174 |
-
grd.addColorStop(1, "rgba(0,0,0,0)");
|
175 |
-
ctx.fillStyle = grd;
|
176 |
-
ctx.fillRect(0, 0, width, height);
|
177 |
-
ctx.restore();
|
178 |
});
|
179 |
}
|
180 |
|
@@ -185,9 +177,10 @@
|
|
185 |
requestAnimationFrame(loop);
|
186 |
}
|
187 |
|
188 |
-
// Initialize
|
189 |
function init() {
|
190 |
resize();
|
|
|
191 |
initStars();
|
192 |
window.addEventListener("resize", () => {
|
193 |
resize();
|
|
|
1 |
+
// Optimized Starfield – Script Only
|
2 |
|
3 |
(() => {
|
4 |
const canvas = document.getElementById("starfield");
|
5 |
const ctx = canvas.getContext("2d");
|
6 |
+
let width, height, bgCanvas, bgCtx;
|
7 |
+
let starSprites = [];
|
8 |
+
let layers = [];
|
|
|
9 |
let comets = [];
|
10 |
+
let speedFactor = 1;
|
11 |
|
12 |
+
// Layer configuration: reduced counts & speeds tuned
|
13 |
+
const layerConfigs = [
|
14 |
+
{ count: 300, speed: 0.5, size: 1.5 },
|
15 |
+
{ count: 200, speed: 1.0, size: 2.5 },
|
16 |
+
{ count: 100, speed: 2.0, size: 4.0 },
|
17 |
];
|
18 |
|
19 |
// Utility
|
20 |
const rand = (min, max) => min + Math.random() * (max - min);
|
21 |
|
22 |
+
// Resize & background cache
|
23 |
function resize() {
|
|
|
24 |
width = window.innerWidth;
|
25 |
height = window.innerHeight;
|
26 |
+
canvas.width = width;
|
27 |
+
canvas.height = height;
|
28 |
+
|
29 |
+
// Cache radial background gradient once
|
30 |
+
bgCanvas = document.createElement("canvas");
|
31 |
+
bgCanvas.width = width;
|
32 |
+
bgCanvas.height = height;
|
33 |
+
bgCtx = bgCanvas.getContext("2d");
|
34 |
+
const grad = bgCtx.createRadialGradient(
|
35 |
+
width / 2, height / 2, 0,
|
36 |
+
width / 2, height / 2, width
|
37 |
+
);
|
38 |
+
grad.addColorStop(0, "rgba(30,30,40,0.3)");
|
39 |
+
grad.addColorStop(1, "black");
|
40 |
+
bgCtx.fillStyle = grad;
|
41 |
+
bgCtx.fillRect(0, 0, width, height);
|
42 |
+
}
|
43 |
+
|
44 |
+
// Pre-render star sprites of different radii
|
45 |
+
function createStarSprites() {
|
46 |
+
const radii = [1.2, 2.5, 4.0];
|
47 |
+
radii.forEach(r => {
|
48 |
+
const size = r * 4;
|
49 |
+
const off = document.createElement("canvas");
|
50 |
+
off.width = off.height = size;
|
51 |
+
const octx = off.getContext("2d");
|
52 |
+
octx.fillStyle = "#fff";
|
53 |
+
octx.shadowColor = "#fff";
|
54 |
+
octx.shadowBlur = r * 1.5;
|
55 |
+
octx.beginPath();
|
56 |
+
octx.arc(size / 2, size / 2, r, 0, Math.PI * 2);
|
57 |
+
octx.fill();
|
58 |
+
starSprites.push({ sprite: off, radius: r });
|
59 |
+
});
|
60 |
}
|
61 |
|
62 |
// Initialize star layers
|
63 |
function initStars() {
|
64 |
+
layers = layerConfigs.map((cfg, idx) => {
|
65 |
const stars = [];
|
66 |
for (let i = 0; i < cfg.count; i++) {
|
67 |
stars.push({
|
68 |
x: rand(-width / 2, width / 2),
|
69 |
y: rand(-height / 2, height / 2),
|
70 |
z: rand(1, width),
|
71 |
+
a: rand(0.2, 1),
|
72 |
+
spriteIndex: idx, // reuse pre-rendered radii
|
73 |
});
|
74 |
}
|
75 |
return { ...cfg, stars };
|
76 |
});
|
77 |
}
|
78 |
|
79 |
+
// Occasionally spawn a simple comet
|
80 |
function maybeComet() {
|
81 |
+
if (Math.random() < 0.003) {
|
82 |
comets.push({
|
83 |
x: rand(0, width),
|
84 |
y: rand(0, height * 0.2),
|
85 |
len: 0,
|
86 |
+
maxLen: rand(80, 200),
|
87 |
angle: Math.PI * 0.25 + rand(-0.2, 0.2),
|
88 |
+
speed: rand(8, 14),
|
89 |
});
|
90 |
}
|
91 |
}
|
92 |
|
93 |
+
// Mouse speed control
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
window.addEventListener("mousemove", e => {
|
95 |
+
speedFactor = 0.5 + (e.clientX / width) * 2.0;
|
96 |
});
|
97 |
|
98 |
+
// Update positions & twinkle
|
99 |
function update() {
|
100 |
+
layers.forEach(layer => {
|
101 |
+
const move = layer.speed * speedFactor;
|
102 |
layer.stars.forEach(s => {
|
103 |
+
s.z -= move;
|
104 |
if (s.z <= 0) {
|
105 |
s.z = width;
|
106 |
s.x = rand(-width / 2, width / 2);
|
107 |
s.y = rand(-height / 2, height / 2);
|
108 |
+
s.a = rand(0.2, 1);
|
|
|
109 |
}
|
110 |
+
// twinkle oscillation
|
111 |
+
s.a += Math.sin(Date.now() / 500 + s.z) * 0.002;
|
112 |
+
s.a = Math.max(0.2, Math.min(1, s.a));
|
113 |
});
|
114 |
});
|
115 |
|
|
|
120 |
c.y += Math.sin(c.angle) * c.speed;
|
121 |
});
|
122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
maybeComet();
|
124 |
}
|
125 |
|
126 |
+
// Draw everything
|
127 |
function draw() {
|
128 |
+
// 1) static background
|
129 |
+
ctx.drawImage(bgCanvas, 0, 0);
|
|
|
130 |
|
131 |
+
// 2) motion trails
|
132 |
+
ctx.fillStyle = "rgba(0,0,0,0.3)";
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
ctx.fillRect(0, 0, width, height);
|
134 |
|
135 |
+
// 3) stars using cached sprites
|
136 |
+
layers.forEach((layer, li) => {
|
137 |
+
const { stars, size } = layer;
|
138 |
+
const { sprite, radius } = starSprites[li];
|
139 |
+
ctx.globalCompositeOperation = "lighter";
|
140 |
+
stars.forEach(s => {
|
141 |
const k = (width / 2) / s.z;
|
142 |
const x = s.x * k + width / 2;
|
143 |
const y = s.y * k + height / 2;
|
144 |
if (x < 0 || x > width || y < 0 || y > height) return;
|
145 |
+
const drawSize = (1 - s.z / width) * size * 2;
|
146 |
+
ctx.globalAlpha = s.a;
|
147 |
+
ctx.drawImage(
|
148 |
+
sprite,
|
149 |
+
x - drawSize,
|
150 |
+
y - drawSize,
|
151 |
+
drawSize * 2,
|
152 |
+
drawSize * 2
|
153 |
+
);
|
|
|
|
|
154 |
});
|
155 |
});
|
156 |
ctx.globalCompositeOperation = "source-over";
|
157 |
+
ctx.globalAlpha = 1;
|
158 |
|
159 |
+
// 4) simple comets (no blur)
|
160 |
comets.forEach(c => {
|
161 |
+
ctx.strokeStyle = "rgba(255,255,255,0.8)";
|
162 |
+
ctx.lineWidth = 1.5;
|
|
|
|
|
|
|
163 |
ctx.beginPath();
|
164 |
ctx.moveTo(c.x, c.y);
|
165 |
ctx.lineTo(
|
|
|
167 |
c.y - Math.sin(c.angle) * c.len
|
168 |
);
|
169 |
ctx.stroke();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
});
|
171 |
}
|
172 |
|
|
|
177 |
requestAnimationFrame(loop);
|
178 |
}
|
179 |
|
180 |
+
// Initialize all
|
181 |
function init() {
|
182 |
resize();
|
183 |
+
createStarSprites();
|
184 |
initStars();
|
185 |
window.addEventListener("resize", () => {
|
186 |
resize();
|