1f591fe2b4
- 完善产品页面布局与交互 - 优化服务详情页用户体验 - 增强新闻模块内容展示 - 改进团队页面设计 - 优化全局样式和响应式布局 - 添加分页组件支持 - 提升性能与SEO优化 - 修复已知问题与改进代码质量
74 lines
1.7 KiB
TypeScript
74 lines
1.7 KiB
TypeScript
'use client';
|
|
|
|
import { motion } from 'framer-motion';
|
|
import { useState, useEffect } from 'react';
|
|
|
|
interface SubtleDotsProps {
|
|
className?: string;
|
|
color?: string;
|
|
count?: number;
|
|
}
|
|
|
|
export function SubtleDots({
|
|
className = '',
|
|
color = 'var(--color-brand-primary)',
|
|
count = 12
|
|
}: SubtleDotsProps) {
|
|
const [dots, setDots] = useState<Array<{
|
|
id: number;
|
|
x: number;
|
|
y: number;
|
|
size: number;
|
|
delay: number;
|
|
}>>([]);
|
|
|
|
/* eslint-disable react-hooks/set-state-in-effect */
|
|
useEffect(() => {
|
|
const newDots = Array.from({ length: count }, (_, i) => ({
|
|
id: i,
|
|
x: 10 + Math.random() * 80,
|
|
y: 10 + Math.random() * 80,
|
|
size: 2 + Math.random() * 3,
|
|
delay: i * 0.3
|
|
}));
|
|
setDots(newDots);
|
|
}, [count]);
|
|
/* eslint-enable react-hooks/set-state-in-effect */
|
|
|
|
if (dots.length === 0) {
|
|
return <div className={`absolute inset-0 pointer-events-none ${className}`} />;
|
|
}
|
|
|
|
return (
|
|
<div className={`absolute inset-0 pointer-events-none ${className}`}>
|
|
{dots.map((dot) => (
|
|
<motion.div
|
|
key={dot.id}
|
|
className="absolute rounded-full"
|
|
style={{
|
|
width: dot.size,
|
|
height: dot.size,
|
|
backgroundColor: color,
|
|
left: `${dot.x}%`,
|
|
top: `${dot.y}%`
|
|
}}
|
|
initial={{ opacity: 0, scale: 0 }}
|
|
animate={{
|
|
opacity: [0, 0.2, 0.2, 0],
|
|
scale: [0, 1, 1, 0]
|
|
}}
|
|
transition={{
|
|
duration: 4,
|
|
delay: dot.delay,
|
|
repeat: Infinity,
|
|
ease: 'easeInOut',
|
|
times: [0, 0.3, 0.7, 1]
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default SubtleDots;
|