From 88beacde1ac91ea1c7e3126650344da06d552ef1 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Sun, 20 Aug 2023 20:04:06 +0200 Subject: [PATCH] Added particles to light bar --- src/components/SearchBar.tsx | 2 +- src/components/utils/Lightbar.css | 9 ++ src/components/utils/Lightbar.tsx | 138 +++++++++++++++++++++++++++++- 3 files changed, 147 insertions(+), 2 deletions(-) diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 9f2b6958..28048e16 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -60,7 +60,7 @@ export function SearchBarInput(props: SearchBarProps) { onFocus={() => setFocused(true)} onChange={(val) => setSearch(val)} value={props.value.searchQuery} - className="text-search-text w-full flex-1 bg-transparent px-4 py-4 pl-12 placeholder-search-placeholder focus:outline-none sm:py-4 sm:pr-2" + className="w-full flex-1 bg-transparent px-4 py-4 pl-12 text-search-text placeholder-search-placeholder focus:outline-none sm:py-4 sm:pr-2" placeholder={props.placeholder} /> diff --git a/src/components/utils/Lightbar.css b/src/components/utils/Lightbar.css index 9cf845d4..c49e144f 100644 --- a/src/components/utils/Lightbar.css +++ b/src/components/utils/Lightbar.css @@ -19,4 +19,13 @@ transform: rotate(180deg) translateZ(0px) translateY(400px); transform-origin: center center; background-repeat: no-repeat; + display: flex; + justify-content: center; + align-items: center; +} + +.lightbar canvas { + width: 40%; + height: 300px; + transform: translateY(-50%) rotate(180deg); } diff --git a/src/components/utils/Lightbar.tsx b/src/components/utils/Lightbar.tsx index 604f7cba..bfcfd592 100644 --- a/src/components/utils/Lightbar.tsx +++ b/src/components/utils/Lightbar.tsx @@ -1,9 +1,145 @@ +import { useEffect, useRef } from "react"; import "./Lightbar.css"; +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(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 | 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 ; +} + export function Lightbar(props: { className?: string }) { return (
-
+
+ +
); }