From ac165af833c6797d629afd70117f930a25673778 Mon Sep 17 00:00:00 2001 From: molvqingtai Date: Fri, 27 Sep 2024 06:00:24 +0800 Subject: [PATCH] perf(options): add meteors effect --- src/app/options/App.tsx | 2 ++ src/components/magicui/meteors.tsx | 44 ++++++++++++++++++++++++++++++ tailwind.config.ts | 11 +++++++- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/components/magicui/meteors.tsx diff --git a/src/app/options/App.tsx b/src/app/options/App.tsx index 94438c2..600b28d 100644 --- a/src/app/options/App.tsx +++ b/src/app/options/App.tsx @@ -2,10 +2,12 @@ import { Toaster } from 'sonner' import Main from './components/Main' import ProfileForm from './components/ProfileForm' import BadgeList from './components/BadgeList' +import Meteors from '@/components/magicui/meteors' function App() { return ( <> +
diff --git a/src/components/magicui/meteors.tsx b/src/components/magicui/meteors.tsx new file mode 100644 index 0000000..4549eb6 --- /dev/null +++ b/src/components/magicui/meteors.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { useEffect, useState } from "react"; + +import { cn } from "@/utils/index"; + +interface MeteorsProps { + number?: number; +} +export const Meteors = ({ number = 20 }: MeteorsProps) => { + const [meteorStyles, setMeteorStyles] = useState>( + [], + ); + + useEffect(() => { + const styles = [...new Array(number)].map(() => ({ + top: -5, + left: Math.floor(Math.random() * window.innerWidth) + "px", + animationDelay: Math.random() * 1 + 0.2 + "s", + animationDuration: Math.floor(Math.random() * 8 + 2) + "s", + })); + setMeteorStyles(styles); + }, [number]); + + return ( + <> + {[...meteorStyles].map((style, idx) => ( + // Meteor Head + + {/* Meteor Tail */} +
+ + ))} + + ); +}; + +export default Meteors; diff --git a/tailwind.config.ts b/tailwind.config.ts index e0516ac..06715da 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -73,12 +73,21 @@ export default { pulse: { '0%, 100%': { boxShadow: '0 0 0 0 var(--pulse-color)' }, '50%': { boxShadow: '0 0 0 8px var(--pulse-color)' } + }, + meteor: { + '0%': { transform: 'rotate(215deg) translateX(0)', opacity: '1' }, + '70%': { opacity: '1' }, + '100%': { + transform: 'rotate(215deg) translateX(-500px)', + opacity: '0' + } } }, animation: { 'accordion-down': 'accordion-down 0.2s ease-out', 'accordion-up': 'accordion-up 0.2s ease-out', - pulse: 'pulse var(--duration) ease-out infinite' + pulse: 'pulse var(--duration) ease-out infinite', + meteor: 'meteor 5s linear infinite' } } },