Skip to content

Commit

Permalink
feat(ui): add desktop about page animations
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonsaldan committed Jan 1, 2025
1 parent 72f24ab commit 5acd678
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 44 deletions.
14 changes: 10 additions & 4 deletions src/components/about-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,12 @@ function About() {
role="list"
className="grid gap-x-8 gap-y-12 sm:grid-cols-2 sm:gap-y-16 xl:col-span-2"
>
{people.map((person) => (
<AnimatedTeamMember key={person.name} person={person} />
{people.map((person, index) => (
<AnimatedTeamMember
key={person.name}
person={person}
index={index}
/>
))}
</ul>
</div>
Expand All @@ -270,10 +274,11 @@ function About() {
<div className="mx-auto mb-32 hidden max-w-7xl gap-20 px-8 sm:grid sm:grid-cols-3 sm:px-0">
<div className="mx-auto max-w-2xl sm:col-span-2 sm:px-0 lg:mx-0 lg:max-w-none">
<div className="-mt-8 sm:-mx-4 sm:columns-2 sm:text-[0] lg:columns-3">
{testimonials.map((testimonial) => (
{testimonials.map((testimonial, index) => (
<AnimatedTestimonial
key={testimonial.author.handle}
testimonial={testimonial}
index={index}
/>
))}
</div>
Expand Down Expand Up @@ -311,10 +316,11 @@ function About() {
</div>
<div className="mx-auto max-w-2xl sm:col-span-2 sm:px-0 lg:mx-0 lg:max-w-none">
<div className="-mt-8 sm:-mx-4 sm:columns-2 sm:text-[0] lg:columns-3">
{testimonials.map((testimonial) => (
{testimonials.map((testimonial, index) => (
<AnimatedTestimonial
key={testimonial.author.handle}
testimonial={testimonial}
index={index}
/>
))}
</div>
Expand Down
89 changes: 63 additions & 26 deletions src/components/about/animated-image-gallery.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,49 @@
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'

const AnimatedImageGallery = () => {
const imageRefs = [useRef<HTMLDivElement>(null), useRef<HTMLDivElement>(null)]
const [isDesktop, setIsDesktop] = useState(false)
const [isLoaded, setIsLoaded] = useState(false)
const imageRefs = [
useRef<HTMLDivElement>(null),
useRef<HTMLDivElement>(null),
useRef<HTMLDivElement>(null),
useRef<HTMLDivElement>(null),
useRef<HTMLDivElement>(null),
]

useEffect(() => {
if (window.innerWidth >= 640) return
setIsDesktop(window.innerWidth >= 640)
setIsLoaded(true)
}, [])

useEffect(() => {
if (!isDesktop || !isLoaded) return

imageRefs.forEach((ref, index) => {
setTimeout(() => {
if (ref.current) {
ref.current.classList.remove('opacity-0', 'translate-y-8')
ref.current.classList.add('opacity-100', 'translate-y-0')
}
}, index * 150)
})
}, [isDesktop, isLoaded])

const observers = imageRefs.map((ref, index) => {
const threshold = 0.5 - index * 0.1
useEffect(() => {
if (isDesktop) return

const mobileAnimatedRefs = [imageRefs[2], imageRefs[4]]

const observers = mobileAnimatedRefs.map((ref, index) => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (entry.isIntersecting && ref.current) {
setTimeout(() => {
if (ref.current) {
ref.current.classList.remove(
'opacity-0',
'translate-x-8',
'-translate-x-8',
index === 0 ? '-translate-x-8' : 'translate-x-8',
)
ref.current.classList.add('opacity-100', 'translate-x-0')
}
Expand All @@ -28,7 +53,7 @@ const AnimatedImageGallery = () => {
})
},
{
threshold,
threshold: 0.2,
rootMargin: '0px 0px -10% 0px',
},
)
Expand All @@ -43,23 +68,41 @@ const AnimatedImageGallery = () => {
return () => {
observers.forEach((observer) => observer.disconnect())
}
}, [])
}, [isDesktop])

const getInitialClasses = (index: number) => {
if (!isLoaded) return 'opacity-0'

if (isDesktop) {
return 'translate-y-8 transform opacity-0 transition-all duration-700 ease-out'
}

if (index === 2) {
return '-translate-x-8 transform opacity-0 transition-all duration-700 ease-out'
}
if (index === 4) {
return 'translate-x-8 transform opacity-0 transition-all duration-700 ease-out'
}
return 'transform-none opacity-100'
}

return (
<>
<div className="ml-auto w-44 flex-none space-y-8 pt-32 sm:ml-0 sm:pt-80 lg:order-last lg:pt-36 xl:order-none xl:pt-80">
<div className="relative">
<img
alt=""
src="/about/1.webp"
className="hidden aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg sm:block"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
<div ref={imageRefs[0]} className={getInitialClasses(0)}>
<img
alt=""
src="/about/1.webp"
className="hidden aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg sm:block"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</div>
</div>
</div>
<div className="mr-auto w-44 flex-none space-y-8 sm:mr-0 sm:pt-52 lg:pt-36">
<div className="relative">
<div className="sm:transform-none sm:opacity-100">
<div ref={imageRefs[1]} className={getInitialClasses(1)}>
<img
alt=""
src="/about/2.webp"
Expand All @@ -69,10 +112,7 @@ const AnimatedImageGallery = () => {
</div>
</div>
<div className="relative">
<div
ref={imageRefs[0]}
className="-translate-x-8 transform opacity-0 transition-all duration-700 ease-out sm:transform-none sm:opacity-100"
>
<div ref={imageRefs[2]} className={getInitialClasses(2)}>
<img
alt=""
src="/about/3.webp"
Expand All @@ -84,7 +124,7 @@ const AnimatedImageGallery = () => {
</div>
<div className="w-44 flex-none space-y-8 pt-32 sm:pt-0">
<div className="relative">
<div className="sm:transform-none sm:opacity-100">
<div ref={imageRefs[3]} className={getInitialClasses(3)}>
<img
alt=""
src="/about/4.webp"
Expand All @@ -94,10 +134,7 @@ const AnimatedImageGallery = () => {
</div>
</div>
<div className="relative">
<div
ref={imageRefs[1]}
className="translate-x-8 transform opacity-0 transition-all duration-700 ease-out sm:transform-none sm:opacity-100"
>
<div ref={imageRefs[4]} className={getInitialClasses(4)}>
<img
alt=""
src="/about/5.webp"
Expand Down
38 changes: 31 additions & 7 deletions src/components/about/animated-team-members.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'

interface TeamMember {
name: string
Expand All @@ -9,19 +9,37 @@ interface TeamMember {

interface AnimatedTeamMemberProps {
person: TeamMember
index: number
}

const AnimatedTeamMembers = ({ person }: AnimatedTeamMemberProps) => {
const AnimatedTeamMembers = ({ person, index }: AnimatedTeamMemberProps) => {
const memberRef = useRef<HTMLLIElement>(null)
const [isDesktop, setIsDesktop] = useState(false)

useEffect(() => {
if (window.innerWidth >= 640) return
setIsDesktop(window.innerWidth >= 640)
}, [])

useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (memberRef.current) {
if (entry.isIntersecting && memberRef.current) {
if (isDesktop) {
setTimeout(() => {
if (memberRef.current) {
const isLeftColumn = index % 2 === 0
memberRef.current.classList.remove(
'opacity-0',
isLeftColumn ? '-translate-x-8' : 'translate-x-8',
)
memberRef.current.classList.add(
'opacity-100',
'translate-x-0',
)
}
}, index * 150)
} else {
memberRef.current.classList.remove('opacity-0', 'translate-y-8')
memberRef.current.classList.add('opacity-100', 'translate-y-0')
}
Expand All @@ -42,12 +60,18 @@ const AnimatedTeamMembers = ({ person }: AnimatedTeamMemberProps) => {
return () => {
observer.disconnect()
}
}, [])
}, [isDesktop, index])

const initialTransform = isDesktop
? index % 2 === 0
? '-translate-x-8'
: 'translate-x-8'
: 'translate-y-8'

return (
<li
ref={memberRef}
className="translate-y-8 transform opacity-0 transition-all duration-700 ease-out sm:transform-none sm:opacity-100"
className={`transform opacity-0 transition-all duration-700 ease-out ${initialTransform}`}
>
<div className="flex items-center gap-x-6">
<div className="group flex items-center gap-x-6">
Expand Down
39 changes: 32 additions & 7 deletions src/components/about/animated-testimonials.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'

interface TestimonialAuthor {
name: string
Expand All @@ -14,19 +14,42 @@ interface Testimonial {

interface AnimatedTestimonialProps {
testimonial: Testimonial
index: number
}

const AnimatedTestimonials = ({ testimonial }: AnimatedTestimonialProps) => {
const ITEMS_PER_COLUMN = 3

const AnimatedTestimonials = ({
testimonial,
index,
}: AnimatedTestimonialProps) => {
const testimonialRef = useRef<HTMLDivElement>(null)
const [isDesktop, setIsDesktop] = useState(false)

useEffect(() => {
if (window.innerWidth >= 640) return
setIsDesktop(window.innerWidth >= 640)
}, [])

useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (testimonialRef.current) {
if (entry.isIntersecting && testimonialRef.current) {
if (isDesktop) {
const columnIndex = Math.floor(index / ITEMS_PER_COLUMN)
setTimeout(() => {
if (testimonialRef.current) {
testimonialRef.current.classList.remove(
'opacity-0',
'translate-y-4',
)
testimonialRef.current.classList.add(
'opacity-100',
'translate-y-0',
)
}
}, columnIndex * 200)
} else {
testimonialRef.current.classList.remove(
'opacity-0',
'translate-y-8',
Expand All @@ -53,12 +76,14 @@ const AnimatedTestimonials = ({ testimonial }: AnimatedTestimonialProps) => {
return () => {
observer.disconnect()
}
}, [])
}, [isDesktop, index])

const initialTransform = isDesktop ? 'translate-y-4' : 'translate-y-8'

return (
<div
ref={testimonialRef}
className="translate-y-8 transform pt-8 opacity-0 transition-all duration-700 ease-out sm:inline-block sm:w-full sm:transform-none sm:px-4 sm:opacity-100"
className={`transform pt-8 opacity-0 transition-all duration-700 ease-out sm:inline-block sm:w-full sm:px-4 ${initialTransform}`}
>
<a target="_blank" href={testimonial.href}>
<figure className="flex flex-col justify-between rounded-2xl bg-gray-50 p-8 text-sm/6">
Expand Down

0 comments on commit 5acd678

Please sign in to comment.