done landing page
This commit is contained in:
191
sections/HowWork.tsx
Normal file
191
sections/HowWork.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
"use client";
|
||||
|
||||
import Headline from "@/components/ui/headline";
|
||||
import { useSplitHeading } from "@/lib/useTextRevealHeading";
|
||||
import Image from "next/image";
|
||||
import { useRef, useEffect } from "react";
|
||||
import { gsap } from "gsap";
|
||||
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const HowWork = () => {
|
||||
const headingRef = useRef<HTMLHeadingElement>(null);
|
||||
const paraRef = useRef<HTMLParagraphElement>(null);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Refs for animation targets
|
||||
const imageRef = useRef<HTMLImageElement>(null);
|
||||
const leftStepsRef = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const rightStepsRef = useRef<(HTMLDivElement | null)[]>([]);
|
||||
|
||||
useSplitHeading(headingRef, paraRef);
|
||||
|
||||
const steps = [
|
||||
{
|
||||
id: "01",
|
||||
text: "Deploy Whispering Trees Pulse sensor directly onto your tree.",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
id: "02",
|
||||
text: "Real-time data automatically streams to Whispering Trees Cloud.",
|
||||
position: "left",
|
||||
},
|
||||
{
|
||||
id: "03",
|
||||
text: "Dashboard displays tree health, history, and irrigation requirements.",
|
||||
position: "right",
|
||||
},
|
||||
{
|
||||
id: "04",
|
||||
text: "Export data or integrate seamlessly with city systems.",
|
||||
position: "right",
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
const tl = gsap.timeline({
|
||||
scrollTrigger: {
|
||||
trigger: imageRef.current,
|
||||
start: "top 70%",
|
||||
toggleActions: "play none none none",
|
||||
},
|
||||
});
|
||||
|
||||
// 1. Image: clip-path from bottom → top
|
||||
if (imageRef.current) {
|
||||
gsap.set(imageRef.current, { clipPath: "inset(100% 0% 0% 0%)" });
|
||||
tl.to(imageRef.current, {
|
||||
clipPath: "inset(0% 0% 0% 0%)",
|
||||
duration: 1.2,
|
||||
ease: "power3.out",
|
||||
});
|
||||
}
|
||||
leftStepsRef.current.forEach((el, i) => {
|
||||
if (el) {
|
||||
gsap.set(el, { opacity: 0, x: -80 });
|
||||
tl.to(
|
||||
el,
|
||||
{
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
duration: 0.8,
|
||||
ease: "power2.out",
|
||||
},
|
||||
i === 0 ? "-=0.8" : "-=0.6" // stagger
|
||||
);
|
||||
}
|
||||
});
|
||||
rightStepsRef.current.forEach((el, i) => {
|
||||
if (el) {
|
||||
gsap.set(el, { opacity: 0, x: 80 });
|
||||
tl.to(
|
||||
el,
|
||||
{
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
duration: 0.8,
|
||||
ease: "power2.out",
|
||||
},
|
||||
i === 0 ? "-=0.8" : "-=0.6"
|
||||
);
|
||||
}
|
||||
});
|
||||
}, containerRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={containerRef}
|
||||
id="how-it-works"
|
||||
className="tab:px-6 relative h-full w-full sm:px-2.5 lg:px-0"
|
||||
>
|
||||
<div className="mx-auto flex h-full w-full max-w-[1420px] flex-col items-center justify-start gap-12 py-8 sm:pt-8">
|
||||
{/* Heading */}
|
||||
<div className="tab:w-9/12 flex flex-col items-center justify-center gap-4 sm:w-full md:w-7/12">
|
||||
<Headline text="How It Works" />
|
||||
<h1
|
||||
ref={headingRef}
|
||||
className="font-switzer text-h-mobile-30 leading-h5 tab:text-h-2-60 tab:leading-h2 text-center font-semibold text-black"
|
||||
>
|
||||
The Whispering Trees{" "}
|
||||
<span className="font-playfair text-brand italic">Process.</span>
|
||||
</h1>
|
||||
<p
|
||||
ref={paraRef}
|
||||
className="font-mium-reg text-b-3-16 leading-b3 text-center tracking-[-0.8px] text-black"
|
||||
>
|
||||
Monitor tree health in real-time and optimize irrigation schedules
|
||||
with our intelligent sensor system, ensuring urban forests thrive
|
||||
while conserving water.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Steps + Image */}
|
||||
<div className="tab:flex-row tab:h-[500px] flex w-full items-center justify-between sm:flex-col">
|
||||
{/* Left Steps */}
|
||||
<div className="tab:w-4/12 flex h-full flex-col items-center justify-between sm:w-full">
|
||||
{steps
|
||||
.filter((s) => s.position === "left")
|
||||
.map((step, i) => (
|
||||
<div
|
||||
key={step.id}
|
||||
ref={(el) => {
|
||||
leftStepsRef.current[i] = el;
|
||||
}}
|
||||
className="tab:h-[200px] tab:w-[350px] flex h-[180px] w-full flex-col items-center gap-4 sm:h-[180px]"
|
||||
>
|
||||
<h2 className="font-switzer text-h-4-34 leading-h4 font-medium text-black">
|
||||
{step.id}
|
||||
</h2>
|
||||
<p className="font-switzer text-b-1-20 leading-b1 text-center font-medium text-black">
|
||||
{step.text}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Center Image */}
|
||||
<div className="tab:mt-0 tab:mb-0 tab:w-4/12 h-full sm:-mt-14 sm:mb-14 sm:w-full">
|
||||
<Image
|
||||
ref={imageRef}
|
||||
alt="Product"
|
||||
width={600}
|
||||
height={600}
|
||||
src="/images/png/product.png"
|
||||
className="h-full w-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right Steps */}
|
||||
<div className="tab:w-4/12 flex h-full flex-col items-center justify-between sm:w-full">
|
||||
{steps
|
||||
.filter((s) => s.position === "right")
|
||||
.map((step, i) => (
|
||||
<div
|
||||
key={step.id}
|
||||
ref={(el) => {
|
||||
rightStepsRef.current[i] = el;
|
||||
}}
|
||||
className="tab:h-[200px] tab:w-[350px] flex h-[180px] w-full flex-col items-center gap-4 sm:h-[180px]"
|
||||
>
|
||||
<h2 className="font-switzer text-h-4-34 leading-h4 font-medium text-black">
|
||||
{step.id}
|
||||
</h2>
|
||||
<p className="font-switzer text-b-1-20 leading-b1 text-center font-medium text-black">
|
||||
{step.text}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default HowWork;
|
||||
Reference in New Issue
Block a user