feat: implement intersection observer for fade-in animations in multiple components
This commit is contained in:
@@ -7,6 +7,7 @@ interface ButtonProps {
|
|||||||
variant?: "default" | "blue" | "blue_alt";
|
variant?: "default" | "blue" | "blue_alt";
|
||||||
shadowEnabled?: boolean;
|
shadowEnabled?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Button(props: ButtonProps) {
|
export default function Button(props: ButtonProps) {
|
||||||
@@ -28,7 +29,10 @@ export default function Button(props: ButtonProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${fluxgore.className} relative inline-block`}>
|
<div
|
||||||
|
style={props.style}
|
||||||
|
className={`${fluxgore.className} relative inline-block`}
|
||||||
|
>
|
||||||
{/* Shadow element */}
|
{/* Shadow element */}
|
||||||
{shadowEnabled && !disabled && (
|
{shadowEnabled && !disabled && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
+101
-5
@@ -12,11 +12,13 @@ function CoverHeading({ children, textPosition }: CoverHeadingProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<h1
|
<h1
|
||||||
className={`${fluxgore.className} text-white relative`}
|
className={`${fluxgore.className} text-white relative animate-fade-in-up`}
|
||||||
style={{
|
style={{
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
fontSize: "clamp(50px, 10vw, 7vw)", // Slightly smaller on all screen sizes
|
fontSize: "clamp(50px, 10vw, 7vw)", // Slightly smaller on all screen sizes
|
||||||
lineHeight: "1",
|
lineHeight: "1",
|
||||||
|
animationDelay: textPosition === "right" ? "0.3s" : "0.1s",
|
||||||
|
animationFillMode: "both",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Shadow layer */}
|
{/* Shadow layer */}
|
||||||
@@ -68,7 +70,7 @@ function CoverHeading({ children, textPosition }: CoverHeadingProps) {
|
|||||||
function DateBox() {
|
function DateBox() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${fluxgore.className} bg-white text-black px-6 py-2 inline-blockt text-3xl md:text-[40px]`}
|
className={`${fluxgore.className} bg-white text-black px-6 py-2 inline-block text-3xl md:text-[40px] animate-bounce-in`}
|
||||||
style={{
|
style={{
|
||||||
transform: "skewX(-15deg)",
|
transform: "skewX(-15deg)",
|
||||||
lineHeight: "1.2",
|
lineHeight: "1.2",
|
||||||
@@ -77,6 +79,8 @@ function DateBox() {
|
|||||||
drop-shadow(-2px -2px 0px rgba(0,0,0,0.3))
|
drop-shadow(-2px -2px 0px rgba(0,0,0,0.3))
|
||||||
`,
|
`,
|
||||||
border: "4px solid black",
|
border: "4px solid black",
|
||||||
|
animationDelay: "0.5s",
|
||||||
|
animationFillMode: "both",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ transform: "skewX(15deg)" }}>
|
<div style={{ transform: "skewX(15deg)" }}>
|
||||||
@@ -91,7 +95,7 @@ function DateBox() {
|
|||||||
function Cover() {
|
function Cover() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="bg-cover bg-center bg-no-repeat relative justify-center items-center py-36"
|
className="bg-cover bg-center bg-no-repeat relative justify-center items-center py-36 animate-fade-in"
|
||||||
style={{ backgroundImage: "url('/images/KV.png')" }}
|
style={{ backgroundImage: "url('/images/KV.png')" }}
|
||||||
>
|
>
|
||||||
<div className="container mx-auto mb-24">
|
<div className="container mx-auto mb-24">
|
||||||
@@ -116,13 +120,21 @@ function Cover() {
|
|||||||
<img
|
<img
|
||||||
src="/images/cover_text.png"
|
src="/images/cover_text.png"
|
||||||
alt="Фестиваль технических видов спорта 2025"
|
alt="Фестиваль технических видов спорта 2025"
|
||||||
className="w-full max-w-3xl mx-auto hidden md:block"
|
className="w-full max-w-3xl mx-auto hidden md:block animate-slide-in-left"
|
||||||
|
style={{
|
||||||
|
animationDelay: "0.2s",
|
||||||
|
animationFillMode: "both",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src="/images/cover_text_mobile.png"
|
src="/images/cover_text_mobile.png"
|
||||||
alt="Фестиваль технических видов спорта 2025"
|
alt="Фестиваль технических видов спорта 2025"
|
||||||
className="w-full max-w-3xl mx-auto md:hidden"
|
className="w-full max-w-3xl mx-auto md:hidden animate-slide-in-left"
|
||||||
|
style={{
|
||||||
|
animationDelay: "0.2s",
|
||||||
|
animationFillMode: "both",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -131,10 +143,94 @@ function Cover() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.location.href = "/about";
|
window.location.href = "/about";
|
||||||
}}
|
}}
|
||||||
|
className="animate-pulse-glow hover:animate-none transition-all duration-300 hover:scale-105"
|
||||||
|
style={{
|
||||||
|
animationDelay: "0.8s",
|
||||||
|
animationFillMode: "both",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
смотреть карту
|
смотреть карту
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style jsx>{`
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in-up {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-in-left {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce-in {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.3) skewX(-15deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05) skewX(-15deg);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
transform: scale(0.9) skewX(-15deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) skewX(-15deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-glow {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 30px rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in {
|
||||||
|
animation: fade-in 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in-up {
|
||||||
|
animation: fade-in-up 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-slide-in-left {
|
||||||
|
animation: slide-in-left 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-bounce-in {
|
||||||
|
animation: bounce-in 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-pulse-glow {
|
||||||
|
animation: pulse-glow 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-10
@@ -1,11 +1,58 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { gothampro } from "@/utils/fonts";
|
import { gothampro } from "@/utils/fonts";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
function Info() {
|
function Info() {
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
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 (
|
return (
|
||||||
<div id="info" className={gothampro.className}>
|
<div id="info" className={gothampro.className}>
|
||||||
|
<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
|
<div
|
||||||
|
ref={containerRef}
|
||||||
className="bg-[#161616] relative overflow-hidden px-4"
|
className="bg-[#161616] relative overflow-hidden px-4"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url('/images/noise.svg')`,
|
backgroundImage: `url('/images/noise.svg')`,
|
||||||
@@ -15,7 +62,10 @@ function Info() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="container mx-auto pt-36">
|
<div className="container mx-auto pt-36">
|
||||||
<div className="flex flex-row">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="flex flex-row opacity-0 translate-y-8 transition-all duration-700"
|
||||||
|
>
|
||||||
<div className="flex-1 basis-1/3">
|
<div className="flex-1 basis-1/3">
|
||||||
<p className="text-white opacity-60 text-sm md:text-base uppercase font-medium">
|
<p className="text-white opacity-60 text-sm md:text-base uppercase font-medium">
|
||||||
[ о нас ]
|
[ о нас ]
|
||||||
@@ -30,7 +80,10 @@ function Info() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="flex flex-row opacity-0 translate-y-8 transition-all duration-700 delay-200"
|
||||||
|
>
|
||||||
<div className="flex-1 basis-2/12"></div>
|
<div className="flex-1 basis-2/12"></div>
|
||||||
|
|
||||||
<div className="flex-col max-w-10/12">
|
<div className="flex-col max-w-10/12">
|
||||||
@@ -41,7 +94,10 @@ function Info() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row mt-8">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="flex flex-row mt-8 opacity-0 translate-y-8 transition-all duration-700 delay-300"
|
||||||
|
>
|
||||||
<div className="flex-1 basis-1/3"></div>
|
<div className="flex-1 basis-1/3"></div>
|
||||||
|
|
||||||
<div className="flex-col max-w-2/3">
|
<div className="flex-col max-w-2/3">
|
||||||
@@ -60,7 +116,10 @@ function Info() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-row mt-8">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="flex flex-row mt-8 opacity-0 translate-y-8 transition-all duration-700 delay-500"
|
||||||
|
>
|
||||||
<div className="flex-1 basis-1/3"></div>
|
<div className="flex-1 basis-1/3"></div>
|
||||||
|
|
||||||
<div className="flex-col md:min-w-2/3 justify-start items-start">
|
<div className="flex-col md:min-w-2/3 justify-start items-start">
|
||||||
@@ -81,27 +140,36 @@ function Info() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="container mx-auto flex flex-col md:flex-row pt-12 md:pt-24 pb-28 md:pb-56 gap-4 md:gap-0">
|
<div className="container mx-auto flex flex-col md:flex-row pt-12 md:pt-24 pb-28 md:pb-56 gap-4 md:gap-0">
|
||||||
<div className="w-full md:w-1/3 flex justify-center md:justify-start">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="w-full md:w-1/3 flex justify-center md:justify-start opacity-0 translate-y-8 transition-all duration-700 delay-700"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/info/moto.png"
|
src="/images/info/moto.png"
|
||||||
alt="moto"
|
alt="moto"
|
||||||
className="w-3/4 md:w-1/2 h-auto object-cover"
|
className="w-3/4 md:w-1/2 h-auto object-cover hover:scale-105 transition-transform duration-300"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full md:w-1/3 flex justify-center">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="w-full md:w-1/3 flex justify-center opacity-0 translate-y-8 transition-all duration-700 delay-900"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/info/podium.jpg"
|
src="/images/info/podium.jpg"
|
||||||
alt="car"
|
alt="car"
|
||||||
className="w-3/4 md:w-full h-auto object-cover"
|
className="w-3/4 md:w-full h-auto object-cover hover:scale-105 transition-transform duration-300"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full md:w-1/3 justify-center md:justify-start">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="flex w-full md:w-1/3 justify-center md:justify-start opacity-0 translate-y-8 transition-all duration-700 delay-1000"
|
||||||
|
>
|
||||||
<div className="w-0 md:w-1/3"></div>
|
<div className="w-0 md:w-1/3"></div>
|
||||||
<div className="w-3/4 md:w-2/3">
|
<div className="w-3/4 md:w-2/3">
|
||||||
<img
|
<img
|
||||||
src="/images/info/jump.png"
|
src="/images/info/jump.png"
|
||||||
alt="jump"
|
alt="jump"
|
||||||
className="w-full h-auto object-cover"
|
className="w-full h-auto object-cover hover:scale-105 transition-transform duration-300"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,37 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { fluxgore } from "@/utils/fonts";
|
import { fluxgore } from "@/utils/fonts";
|
||||||
|
import Button from "./Button";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
function Partners() {
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
id="partners"
|
id="partners"
|
||||||
@@ -13,40 +43,82 @@ function Partners() {
|
|||||||
backgroundBlendMode: "overlay",
|
backgroundBlendMode: "overlay",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<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="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="opacity-0 translate-y-8 transition-all duration-700"
|
||||||
|
>
|
||||||
<h1
|
<h1
|
||||||
className={`${fluxgore.className} text-4xl sm:text-5xl md:text-6xl lg:text-7xl text-white relative`}
|
className={`${fluxgore.className} text-4xl sm:text-5xl md:text-6xl lg:text-7xl text-white relative`}
|
||||||
>
|
>
|
||||||
партнеры
|
партнеры
|
||||||
</h1>
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
<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
|
||||||
|
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
|
<img
|
||||||
src="/logos/dep.svg"
|
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"
|
alt="Dep Logo"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src="/logos/mos.svg"
|
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"
|
alt="Mos Logo"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src="/logos/raf.png"
|
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"
|
alt="Raf Logo"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src="/logos/ctvs.png"
|
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"
|
alt="Ctvs Logo"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
src="/logos/smp.png"
|
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"
|
alt="SMP Logo"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { fluxgore, gothampro } from "@/utils/fonts";
|
import { fluxgore, gothampro } from "@/utils/fonts";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
interface VideoStatsProps {
|
interface VideoStatsProps {
|
||||||
number: string;
|
number: string;
|
||||||
@@ -26,6 +27,34 @@ function VideoStats(props: VideoStatsProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Video() {
|
function Video() {
|
||||||
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
className="bg-[#161616] relative overflow-hidden pt-10 md:pt-20"
|
className="bg-[#161616] relative overflow-hidden pt-10 md:pt-20"
|
||||||
@@ -36,6 +65,22 @@ function Video() {
|
|||||||
backgroundBlendMode: "overlay",
|
backgroundBlendMode: "overlay",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<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>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
className="absolute top-0 right-0 object-cover"
|
className="absolute top-0 right-0 object-cover"
|
||||||
src="/images/video/arrows.svg"
|
src="/images/video/arrows.svg"
|
||||||
@@ -43,7 +88,10 @@ function Video() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="flex flex-col md:flex-row space-y-6 md:space-y-0 md:space-x-16">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="flex flex-col md:flex-row space-y-6 md:space-y-0 md:space-x-16 opacity-0 translate-y-8 transition-all duration-700"
|
||||||
|
>
|
||||||
<h1
|
<h1
|
||||||
className={`${fluxgore.className} text-4xl md:text-7xl text-white relative`}
|
className={`${fluxgore.className} text-4xl md:text-7xl text-white relative`}
|
||||||
>
|
>
|
||||||
@@ -59,14 +107,17 @@ function Video() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex mt-8 md:mt-14">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="flex mt-8 md:mt-14 opacity-0 translate-y-8 transition-all duration-700 delay-200"
|
||||||
|
>
|
||||||
<iframe
|
<iframe
|
||||||
src="https://vkvideo.ru/video_ext.php?oid=-23293707&id=456242039&hd=2"
|
src="https://vkvideo.ru/video_ext.php?oid=-23293707&id=456242039&hd=2"
|
||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
referrerPolicy="strict-origin-when-cross-origin"
|
referrerPolicy="strict-origin-when-cross-origin"
|
||||||
allowFullScreen
|
allowFullScreen
|
||||||
className="w-full h-auto aspect-video"
|
className="w-full h-auto aspect-video hover:scale-[1.02] transition-transform duration-300"
|
||||||
style={{
|
style={{
|
||||||
clipPath:
|
clipPath:
|
||||||
"polygon(30px 0, 100% 0, 100% calc(100% - 30px), calc(100% - 30px) 100%, 0 100%, 0 30px)",
|
"polygon(30px 0, 100% 0, 100% calc(100% - 30px), calc(100% - 30px) 100%, 0 100%, 0 30px)",
|
||||||
@@ -74,19 +125,30 @@ function Video() {
|
|||||||
></iframe>
|
></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-24 mt-16 md:mt-36 pb-32 md:pb-64">
|
<div
|
||||||
|
ref={addToRefs}
|
||||||
|
className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-24 mt-16 md:mt-36 pb-32 md:pb-64 opacity-0 translate-y-8 transition-all duration-700 delay-500"
|
||||||
|
>
|
||||||
|
<div className="hover:scale-105 transition-transform duration-300">
|
||||||
<VideoStats number="260" label="[ единиц техники ]" />
|
<VideoStats number="260" label="[ единиц техники ]" />
|
||||||
|
</div>
|
||||||
|
<div className="hover:scale-105 transition-transform duration-300">
|
||||||
<VideoStats
|
<VideoStats
|
||||||
number="11"
|
number="11"
|
||||||
label="[ дисциплин технических видов спорта ]"
|
label="[ дисциплин технических видов спорта ]"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="hover:scale-105 transition-transform duration-300">
|
||||||
<VideoStats number="24 K" label="[ л.с. суммарной мощности ]" />
|
<VideoStats number="24 K" label="[ л.с. суммарной мощности ]" />
|
||||||
|
</div>
|
||||||
|
<div className="hover:scale-105 transition-transform duration-300">
|
||||||
<VideoStats
|
<VideoStats
|
||||||
number="350+"
|
number="350+"
|
||||||
label="[ спортсменов-участников соревнований ]"
|
label="[ спортсменов-участников соревнований ]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
className="absolute bottom-0 object-cover w-full"
|
className="absolute bottom-0 object-cover w-full"
|
||||||
|
|||||||
Reference in New Issue
Block a user