1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2025-01-10 17:47:41 +01:00
smov/src/components/utils/Lightbar.tsx

147 lines
3.2 KiB
TypeScript
Raw Normal View History

2023-08-20 20:04:06 +02:00
import { useEffect, useRef } from "react";
2023-08-20 17:59:46 +02:00
import "./Lightbar.css";
2023-08-20 20:04:06 +02:00
class Particle {
x = 0;
y = 0;
radius = 0;
direction = 0;
speed = 0;
lifetime = 0;
ran = 0;
constructor(canvas: HTMLCanvasElement) {
this.reset(canvas);
this.initialize(canvas);
}
reset(canvas: HTMLCanvasElement) {
this.x = Math.round((Math.random() * canvas.width) / 2 + canvas.width / 4);
this.y = Math.random() * 100 + 5;
this.radius = 1 + Math.floor(Math.random() * 0.5);
this.direction = -((Math.random() * Math.PI) / 2) + Math.PI / 4;
this.speed = 0.02 + Math.random() * 0.08;
const second = 60;
this.lifetime = second * 3 + Math.random() * (second * 30);
this.ran = 0;
}
initialize(canvas: HTMLCanvasElement) {
this.ran = Math.random() * this.lifetime;
const baseSpeed = this.speed;
this.speed = Math.random() * this.lifetime * baseSpeed;
this.update(canvas);
this.speed = baseSpeed;
}
update(canvas: HTMLCanvasElement) {
this.ran += 1;
const addX = this.speed * Math.sin(this.direction);
const addY = this.speed * Math.cos(this.direction);
this.x += addX;
this.y += addY;
if (this.ran > this.lifetime) {
this.reset(canvas);
}
}
render(canvas: HTMLCanvasElement) {
const ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.save();
ctx.beginPath();
const x = this.ran / this.lifetime;
const o = (x - x * x) * 4;
ctx.globalAlpha = Math.max(0, o * 0.8);
ctx.ellipse(
this.x,
this.y,
this.radius,
this.radius * 1.5,
this.direction,
0,
Math.PI * 2
);
ctx.fillStyle = "white";
ctx.fill();
ctx.restore();
}
}
function ParticlesCanvas() {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
if (!canvasRef.current) return;
const canvas = canvasRef.current;
const particles: Particle[] = [];
canvas.width = canvas.scrollWidth;
canvas.height = canvas.scrollHeight;
for (let i = 0; i < 20; i += 1) {
const particle = new Particle(canvas);
particles.push(particle);
}
let shouldTick = true;
let handle: ReturnType<typeof requestAnimationFrame> | null = null;
function particlesLoop() {
const ctx = canvas.getContext("2d");
if (!ctx) return;
if (shouldTick) {
for (const particle of particles) {
particle.update(canvas);
}
shouldTick = false;
}
canvas.width = canvas.scrollWidth;
canvas.height = canvas.scrollHeight;
for (const particle of particles) {
particle.render(canvas);
}
handle = requestAnimationFrame(particlesLoop);
}
const interval = setInterval(() => {
shouldTick = true;
}, 1e3 / 120); // tick 120 times a sec
particlesLoop();
return () => {
if (handle) cancelAnimationFrame(handle);
clearInterval(interval);
};
}, []);
return <canvas className="particles" ref={canvasRef} />;
}
2023-08-20 17:59:46 +02:00
export function Lightbar(props: { className?: string }) {
return (
<div className={props.className}>
2023-08-20 20:04:06 +02:00
<div className="lightbar">
<ParticlesCanvas />
2023-08-20 21:03:59 +02:00
<div className="lightbar-visual" />
2023-08-20 20:04:06 +02:00
</div>
2023-08-20 17:59:46 +02:00
</div>
);
}