98 lines
2.2 KiB
Plaintext
98 lines
2.2 KiB
Plaintext
<canvas
|
|
id="starfield"
|
|
class="fixed inset-0 w-full h-full pointer-events-none"
|
|
style="z-index: 0;"
|
|
aria-hidden="true"
|
|
></canvas>
|
|
|
|
<script>
|
|
(function () {
|
|
const canvas = document.getElementById("starfield") as HTMLCanvasElement;
|
|
if (!canvas) return;
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
if (!ctx) return;
|
|
|
|
let width = 0;
|
|
let height = 0;
|
|
let stars: { x: number; y: number; size: number; speed: number; opacity: number; phase: number }[] = [];
|
|
let animationId: number;
|
|
|
|
const STAR_COUNT = 180;
|
|
const TWINKLE_SPEED = 0.0008;
|
|
|
|
function resize() {
|
|
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
width = window.innerWidth;
|
|
height = window.innerHeight;
|
|
canvas.width = width * dpr;
|
|
canvas.height = height * dpr;
|
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
}
|
|
|
|
function initStars() {
|
|
stars = [];
|
|
for (let i = 0; i < STAR_COUNT; i++) {
|
|
stars.push({
|
|
x: Math.random() * width,
|
|
y: Math.random() * height,
|
|
size: Math.random() < 0.95 ? 1 : Math.random() < 0.7 ? 1.5 : 2,
|
|
speed: 0.02 + Math.random() * 0.04,
|
|
opacity: 0.15 + Math.random() * 0.55,
|
|
phase: Math.random() * Math.PI * 2,
|
|
});
|
|
}
|
|
}
|
|
|
|
function draw(time: number) {
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
for (const star of stars) {
|
|
const twinkle =
|
|
0.6 + 0.4 * Math.sin(time * TWINKLE_SPEED + star.phase);
|
|
const alpha = star.opacity * twinkle;
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(star.x, star.y, star.size, 0, Math.PI * 2);
|
|
ctx.fillStyle = `rgba(210, 210, 220, ${alpha})`;
|
|
ctx.fill();
|
|
|
|
star.y -= star.speed;
|
|
if (star.y < -2) {
|
|
star.y = height + 2;
|
|
star.x = Math.random() * width;
|
|
}
|
|
}
|
|
|
|
animationId = requestAnimationFrame(draw);
|
|
}
|
|
|
|
function start() {
|
|
resize();
|
|
initStars();
|
|
animationId = requestAnimationFrame(draw);
|
|
}
|
|
|
|
function stop() {
|
|
cancelAnimationFrame(animationId);
|
|
}
|
|
|
|
start();
|
|
|
|
window.addEventListener("resize", () => {
|
|
stop();
|
|
resize();
|
|
initStars();
|
|
start();
|
|
});
|
|
|
|
document.addEventListener("visibilitychange", () => {
|
|
if (document.hidden) {
|
|
stop();
|
|
} else {
|
|
start();
|
|
}
|
|
});
|
|
})();
|
|
</script>
|