feat: implement intersection observer for fade-in animations in multiple components

This commit is contained in:
2025-08-05 14:47:13 +05:00
parent 29e7ed77b9
commit 99b2824421
5 changed files with 344 additions and 42 deletions
+84 -12
View File
@@ -1,7 +1,37 @@
/* eslint-disable @next/next/no-img-element */
import { fluxgore } from "@/utils/fonts";
import Button from "./Button";
import { useEffect, useRef } from "react";
function Partners() {
const elementsRef = useRef<(HTMLDivElement | null)[]>([]);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("animate-fade-in-up");
entry.target.classList.remove("opacity-0", "translate-y-8");
}
});
},
{ threshold: 0.1, rootMargin: "0px 0px -50px 0px" }
);
elementsRef.current.forEach((el) => {
if (el) observer.observe(el);
});
return () => observer.disconnect();
}, []);
const addToRefs = (el: HTMLDivElement | null) => {
if (el && !elementsRef.current.includes(el)) {
elementsRef.current.push(el);
}
};
return (
<div
id="partners"
@@ -13,40 +43,82 @@ function Partners() {
backgroundBlendMode: "overlay",
}}
>
<div className="container mx-auto px-4">
<h1
className={`${fluxgore.className} text-4xl sm:text-5xl md:text-6xl lg:text-7xl text-white relative`}
>
партнеры
</h1>
<style jsx>{`
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in-up {
animation: fadeInUp 0.6s ease-out forwards;
}
`}</style>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-8 sm:gap-12 md:gap-16 lg:gap-24 mt-8 sm:mt-12 lg:mt-16">
<div className="container mx-auto px-4">
<div
ref={addToRefs}
className="opacity-0 translate-y-8 transition-all duration-700"
>
<h1
className={`${fluxgore.className} text-4xl sm:text-5xl md:text-6xl lg:text-7xl text-white relative`}
>
партнеры
</h1>
</div>
<div
ref={addToRefs}
className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-8 sm:gap-12 md:gap-16 lg:gap-24 mt-8 sm:mt-12 lg:mt-16 opacity-0 translate-y-8 transition-all duration-700 delay-200"
>
<img
src="/logos/dep.svg"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain hover:scale-110 transition-transform duration-300"
alt="Dep Logo"
/>
<img
src="/logos/mos.svg"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain hover:scale-110 transition-transform duration-300"
alt="Mos Logo"
/>
<img
src="/logos/raf.png"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain hover:scale-110 transition-transform duration-300"
alt="Raf Logo"
/>
<img
src="/logos/ctvs.png"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain hover:scale-110 transition-transform duration-300"
alt="Ctvs Logo"
/>
<img
src="/logos/smp.png"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain"
className="w-auto h-16 sm:h-20 lg:h-24 object-contain hover:scale-110 transition-transform duration-300"
alt="SMP Logo"
/>
</div>
<div
ref={addToRefs}
className="opacity-0 translate-y-8 transition-all duration-700 delay-500"
>
<Button
onClick={() => {
const mail = "mailto:ftvs.partners@yandex.ru";
window.location.href = mail;
}}
variant="blue"
className="mt-12 sm:mt-16 lg:mt-20"
shadowEnabled={false}
>
стать партнером
</Button>
</div>
</div>
</div>
);