Files
bedolaga-cabinet/src/components/ui/backgrounds/meteors.tsx
Fringg a933f661e4 perf: optimize animated backgrounds for mobile — reduce GPU load and memory pressure
- Add useAnimationLoop hook with FPS throttling (30fps mobile/60fps desktop),
  Page Visibility API pause, and Telegram WebApp activated/deactivated events
- Add useAnimationPause hook for CSS/Framer Motion components
- Validate animation config with type allowlist, numeric clamping, JSON size guard
- Move ctx.setTransform from per-frame to init/resize across all canvas backgrounds
- Move ctx.lineCap to init/resize in vortex, replace Float32Array.set with direct writes
- Pre-compute shooting star delay instead of per-frame Math.random
- Cap DPR to 1.5 on mobile, reduce particle counts, cap blur to 4px
- Disable aurora CSS animation on mobile, remove blur on mobile spotlight
- Remove mix-blend-hard-light on mobile gradient-animation
- Add NaN/Infinity guard to clampNumber, remove dead CSS variables
- Add conditional unmount for Framer Motion backgrounds when paused
- Add animationPlayState pause for CSS-animated backgrounds
2026-03-01 22:36:57 +03:00

54 lines
1.7 KiB
TypeScript

import { useMemo } from 'react';
import { sanitizeColor, clampNumber } from './types';
import { useAnimationPause } from '@/hooks/useAnimationLoop';
interface Props {
settings: Record<string, unknown>;
}
export default function Meteors({ settings }: Props) {
const count = clampNumber(settings.count, 1, 50, 20);
const meteorColor = sanitizeColor(settings.meteorColor, '#ffffff');
const paused = useAnimationPause();
const meteors = useMemo(
() =>
Array.from({ length: count }, (_, i) => ({
id: i,
left: `${Math.random() * 100}%`,
delay: `${Math.random() * 5}s`,
duration: `${2 + Math.random() * 6}s`,
size: `${1 + Math.random()}px`,
})),
[count],
);
return (
<div className="absolute inset-0 overflow-hidden">
{meteors.map((meteor) => (
<span
key={meteor.id}
className="absolute top-0 rotate-[215deg] animate-meteor-effect rounded-[9999px]"
style={{
left: meteor.left,
animationDelay: meteor.delay,
animationDuration: meteor.duration,
animationPlayState: paused ? 'paused' : 'running',
width: meteor.size,
height: meteor.size,
boxShadow: `0 0 0 1px rgba(255,255,255,0.05), 0 0 2px 1px ${meteorColor}20, 0 0 20px 2px ${meteorColor}40`,
background: meteorColor,
}}
>
<div
className="pointer-events-none absolute top-1/2 -z-10 h-px w-[50px] -translate-y-1/2"
style={{
background: `linear-gradient(to right, ${meteorColor}, transparent)`,
}}
/>
</span>
))}
</div>
);
}