- Introduced DrawLine component for drawing animations. - Added LazyRevealImage component for lazy loading images with reveal animations. - Implemented TextDiffuseReveal for animated text reveals with staggered effects. - Created TopProgressBar for a dynamic loading indicator. - Added PointCloud mock for testing animations. - Developed useReducedMotion hook to handle user preferences for reduced motion. - Centralized animation configurations in config.ts for easier management. - Defined animation variants in variants.ts for consistent animation effects. - Established AnimationProvider to supply global animation settings. - Created tests for AnimationProvider and useReducedMotion hook. - Added SVG assets for branding and UI elements. - Documented frontend review plan and hero circuit board animation plan.
115 lines
3.2 KiB
TypeScript
115 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* CompanyLogos Component
|
|
*
|
|
* Displays an infinite-scrolling banner of company logos after the header.
|
|
* Features smooth horizontal animation with reduced motion support.
|
|
*
|
|
* Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8
|
|
*/
|
|
|
|
import { motion } from "framer-motion";
|
|
import Image from "next/image";
|
|
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
|
|
|
|
// TypeScript interface for company logos
|
|
export interface CompanyLogo {
|
|
name: string; // Company name
|
|
imagePath: string; // Path to logo image (SVG preferred)
|
|
alt: string; // Alt text for accessibility
|
|
}
|
|
|
|
// Company logos data
|
|
const companies: CompanyLogo[] = [
|
|
{
|
|
name: "Apolo Energia",
|
|
imagePath: "/logos/apolo.svg",
|
|
alt: "Apolo Energia logo",
|
|
},
|
|
{
|
|
name: "Julia Beauty Lashes",
|
|
imagePath: "/logos/julia.svg",
|
|
alt: "Julia Beauty Lashes logo",
|
|
},
|
|
{
|
|
name: "Innovo",
|
|
imagePath: "/logos/innovo.svg",
|
|
alt: "Innovo logo",
|
|
},
|
|
{
|
|
name: "TeraByteShop",
|
|
imagePath: "/logos/terabyte.svg",
|
|
alt: "TeraByteShop logo",
|
|
},
|
|
{
|
|
name: "Pichau",
|
|
imagePath: "/logos/pichau.svg",
|
|
alt: "Pichau logo",
|
|
},
|
|
];
|
|
|
|
export default function CompanyLogos() {
|
|
const shouldReduceMotion = useReducedMotion();
|
|
|
|
// Duplicate logo array for seamless infinite scroll
|
|
const duplicatedLogos = [...companies, ...companies];
|
|
|
|
// Static grid for reduced motion preference
|
|
if (shouldReduceMotion) {
|
|
return (
|
|
<section className="w-full border-b border-[rgba(255,255,255,0.06)] bg-[var(--paper)] overflow-hidden">
|
|
<div className="container mx-auto px-sp-6 py-sp-8 md:px-sp-10 lg:px-sp-16">
|
|
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-sp-6 items-center justify-items-center">
|
|
{companies.map((logo) => (
|
|
<div key={logo.name} className="flex items-center justify-center">
|
|
<Image
|
|
src={logo.imagePath}
|
|
alt={logo.alt}
|
|
width={150}
|
|
height={48}
|
|
className="h-12 w-auto object-contain grayscale opacity-60"
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
// Animated infinite scroll
|
|
return (
|
|
<section className="w-full border-b border-[rgba(255,255,255,0.06)] bg-[var(--paper)] overflow-hidden">
|
|
<div className="py-sp-8">
|
|
<motion.div
|
|
className="flex gap-sp-12 items-center"
|
|
animate={{
|
|
x: [0, "-50%"],
|
|
}}
|
|
transition={{
|
|
duration: 30,
|
|
repeat: Infinity,
|
|
ease: "linear",
|
|
}}
|
|
>
|
|
{duplicatedLogos.map((logo, index) => (
|
|
<div
|
|
key={`${logo.name}-${index}`}
|
|
className="flex-shrink-0 flex items-center justify-center"
|
|
>
|
|
<Image
|
|
src={logo.imagePath}
|
|
alt={logo.alt}
|
|
width={150}
|
|
height={48}
|
|
className="h-12 w-auto object-contain grayscale hover:grayscale-0 opacity-60 hover:opacity-100 transition-all duration-300"
|
|
/>
|
|
</div>
|
|
))}
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|