@@ -14,6 +14,7 @@ import {
1414import { Repos } from "./assets/Data.tsx" ;
1515import HeroMainText from "./assets/HeroMainText.tsx" ;
1616import Section from "./assets/Section.tsx" ;
17+ import { MainAnimationDecorator } from "./assets/MainAnimationDecorator.tsx" ;
1718
1819export interface ProjectWrapper {
1920 name : string
@@ -59,14 +60,114 @@ interface Props {
5960 search : string
6061}
6162
63+ declare global {
64+ interface Window {
65+ _particles ?: Array < {
66+ sx : number ;
67+ sy : number ;
68+ x : number ;
69+ y : number ;
70+ vx : number ;
71+ vy : number ;
72+ trail : Array < { x : number ; y : number ; alpha : number } > ;
73+ } > ;
74+ }
75+ }
76+
77+ const renderFunc = ( ctx : CanvasRenderingContext2D , width : number , height : number ) => {
78+ const cssVariable = getComputedStyle ( document . documentElement ) . getPropertyValue ( '--p' ) . trim ( ) ;
79+
80+ if ( ! window . _particles ) {
81+ window . _particles = [ ] ;
82+
83+ const numParticles = 100 ;
84+
85+ while ( window . _particles ! . length < numParticles ) {
86+ const sx = Math . random ( ) * width ;
87+ const sy = Math . random ( ) * height ;
88+
89+ window . _particles ! . push ( {
90+ x : sx ,
91+ y : sy ,
92+ sx : sx ,
93+ sy : sy ,
94+ vx : ( 0.15 + Math . random ( ) * 0.1 ) * ( width / 500 ) ,
95+ vy : ( 0.15 + Math . random ( ) * 0.1 ) * ( height / 500 ) ,
96+ trail : [ ]
97+ } ) ;
98+ }
99+
100+ window . _particles . sort ( ( a , b ) => {
101+ const distA = Math . hypot ( width - a . x , height - a . y ) ;
102+ const distB = Math . hypot ( width - b . x , height - b . y ) ;
103+ return distB - distA ;
104+ } ) ;
105+ }
106+
107+ const particles = window . _particles ;
108+
109+ ctx . clearRect ( 0 , 0 , width , height ) ;
110+
111+ particles . forEach ( ( p ) => {
112+ p . x += p . vx ;
113+ p . y += p . vy ;
114+
115+ p . trail . push ( { x : p . x , y : p . y , alpha : 1 } ) ;
116+ if ( p . trail . length > 60 ) p . trail . shift ( ) ;
117+
118+ p . trail . forEach ( point => {
119+ point . alpha *= 0.96 ;
120+ } ) ;
121+
122+ for ( let i = 0 ; i < p . trail . length - 1 ; i ++ ) {
123+ const a = p . trail [ i ] ;
124+ const b = p . trail [ i + 1 ] ;
125+ ctx . strokeStyle = `oklch(${ cssVariable } / ${ a . alpha * 0.2 } )` ;
126+
127+ ctx . lineWidth = 2 ;
128+ ctx . beginPath ( ) ;
129+ ctx . moveTo ( a . x , a . y ) ;
130+ ctx . lineTo ( b . x , b . y ) ;
131+ ctx . stroke ( ) ;
132+ }
133+
134+ ctx . beginPath ( ) ;
135+ ctx . arc ( p . x , p . y , 1.5 , 0 , Math . PI * 2 ) ;
136+ ctx . fillStyle = `oklch(${ cssVariable } / 0.4)` ;
137+ ctx . fill ( ) ;
138+
139+ if ( p . x > width + 20 || p . y > height + 20 ) {
140+ const { x, y } = getRandomXToRespawn ( width , height ) ;
141+ p . x = x ;
142+ p . y = y ;
143+ p . vx = ( 0.15 + Math . random ( ) * 0.1 ) * ( width / 500 ) ;
144+ p . vy = ( 0.15 + Math . random ( ) * 0.1 ) * ( height / 500 ) ;
145+ p . trail = [ ] ;
146+ }
147+ } ) ;
148+ }
149+
150+ function getRandomXToRespawn ( width : number , height : number ) : { x : number , y : number } {
151+ let currX = Math . random ( ) * width ;
152+ let currY = Math . random ( ) * height ;
153+
154+ while ( currY > height * 0.2 && currX > width * 0.2 || currX > width * 0.2 && currY > height * 0.2 ) {
155+ currY = Math . random ( ) * height ;
156+ currX = Math . random ( ) * width ;
157+ }
158+
159+ return { x : currX , y : currY } ;
160+ }
161+
62162function HeroElement ( props : Props ) {
63163 if ( props . search === "" ) {
64164 return (
65165 < >
66166 < div className = "hero back-grid min-h-screen" >
167+ < MainAnimationDecorator renderFunction = { renderFunc } />
67168 < div className = "hero-content text-center" >
68169 < div className = "max-w-md flex flex-col items-center justify-center" >
69- < div className = "mb-10 avatar online " >
170+ < div className = "mb-10 avatar" >
70171 < div className = "w-20 rounded-full" >
71172 < img src = "https://avatars.githubusercontent.com/u/74710895" alt = "Profile Picture"
72173 className = "rounded-full w-20" />
0 commit comments