79 lines
2.1 KiB
JavaScript
79 lines
2.1 KiB
JavaScript
'use client';
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
export default function Starfield({ className = '', animated = false }) {
|
|
const canvasRef = useRef(null);
|
|
|
|
useEffect(() => {
|
|
const canvas = canvasRef.current;
|
|
if (!canvas) return;
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) return;
|
|
|
|
let raf = null;
|
|
let width = 0;
|
|
let height = 0;
|
|
let stars = [];
|
|
|
|
const createStars = () => {
|
|
const count = Math.max(90, Math.floor((width * height) / 16000));
|
|
stars = Array.from({ length: count }, () => ({
|
|
x: Math.random() * width,
|
|
y: Math.random() * height,
|
|
r: Math.random() * 1.4 + 0.4,
|
|
a: Math.random() * 0.7 + 0.2,
|
|
v: Math.random() * 0.08 + 0.02,
|
|
}));
|
|
};
|
|
|
|
const resize = () => {
|
|
const dpr = window.devicePixelRatio || 1;
|
|
width = canvas.clientWidth;
|
|
height = canvas.clientHeight;
|
|
canvas.width = Math.floor(width * dpr);
|
|
canvas.height = Math.floor(height * dpr);
|
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
createStars();
|
|
draw();
|
|
};
|
|
|
|
const draw = () => {
|
|
ctx.clearRect(0, 0, width, height);
|
|
const bg = ctx.createLinearGradient(0, 0, 0, height);
|
|
bg.addColorStop(0, '#0b0720');
|
|
bg.addColorStop(1, '#05030f');
|
|
ctx.fillStyle = bg;
|
|
ctx.fillRect(0, 0, width, height);
|
|
|
|
for (const s of stars) {
|
|
ctx.beginPath();
|
|
ctx.fillStyle = `rgba(220,225,255,${s.a})`;
|
|
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
};
|
|
|
|
const tick = () => {
|
|
for (const s of stars) {
|
|
s.a += (Math.random() - 0.5) * s.v;
|
|
if (s.a < 0.15) s.a = 0.15;
|
|
if (s.a > 0.95) s.a = 0.95;
|
|
}
|
|
draw();
|
|
raf = window.requestAnimationFrame(tick);
|
|
};
|
|
|
|
resize();
|
|
window.addEventListener('resize', resize);
|
|
if (animated) raf = window.requestAnimationFrame(tick);
|
|
|
|
return () => {
|
|
window.removeEventListener('resize', resize);
|
|
if (raf) window.cancelAnimationFrame(raf);
|
|
};
|
|
}, [animated]);
|
|
|
|
return <canvas ref={canvasRef} className={className} aria-hidden="true" />;
|
|
}
|