Skip to content

Commit

Permalink
feat(about): add mobile animations
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonsaldan committed Jan 1, 2025
1 parent 321767f commit d052b3c
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 118 deletions.
128 changes: 12 additions & 116 deletions src/components/about-page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use client'
import { Navbar } from '@/components/about-navbar'
import { Navbar } from '@/components/about/about-navbar'
import AnimatedImageGallery from '@/components/about/animated-image-gallery'
import AnimatedTeamMember from '@/components/about/animated-team-members'
import AnimatedTestimonial from '@/components/about/animated-testimonials'
import { Container } from '@/components/container'
import { Footer } from '@/components/footer'
import { Link } from '@/components/link'
Expand Down Expand Up @@ -198,52 +201,7 @@ function About() {
</p>
</div>
<div className="mt-14 flex justify-end gap-8 sm:-mt-44 sm:justify-start sm:pl-20 lg:mt-0 lg:pl-0">
<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="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</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">
<img
alt=""
src="/about/2.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</div>
<div className="relative">
<img
alt=""
src="/about/3.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</div>
</div>
<div className="w-44 flex-none space-y-8 pt-32 sm:pt-0">
<div className="relative">
<img
alt=""
src="/about/4.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</div>
<div className="relative">
<img
alt=""
src="/about/5.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</div>
</div>
<AnimatedImageGallery />
</div>
</div>
</div>
Expand Down Expand Up @@ -303,27 +261,7 @@ function About() {
className="grid gap-x-8 gap-y-12 sm:grid-cols-2 sm:gap-y-16 xl:col-span-2"
>
{people.map((person) => (
<li key={person.name}>
<div className="flex items-center gap-x-6">
<div className="group flex items-center gap-x-6">
<a target="_blank" href={person.href}>
<img
alt=""
src={person.imageUrl}
className="size-16 rounded-full"
/>
</a>
<div>
<h3 className="text-base/7 font-semibold tracking-tight text-gray-900">
{person.name}
</h3>
<p className="bg-gradient-to-r from-[#ff8a5b] from-[28%] via-[#fe7e98] via-[70%] to-[#f984ff] bg-clip-text text-sm/6 font-normal text-transparent">
{person.role}
</p>
</div>
</div>
</div>
</li>
<AnimatedTeamMember key={person.name} person={person} />
))}
</ul>
</div>
Expand All @@ -333,31 +271,10 @@ function About() {
<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) => (
<div
<AnimatedTestimonial
key={testimonial.author.handle}
className="pt-8 sm:inline-block sm:w-full sm:px-4"
>
<a target="_blank" href={testimonial.href}>
<figure className="flex flex-col justify-between rounded-2xl bg-gray-50 p-8 text-sm/6">
<blockquote className="flex-1 text-gray-900">
<p className="h-12 overflow-hidden">{`“${testimonial.body}”`}</p>
</blockquote>
<figcaption className="mt-6 flex items-center gap-x-4">
<img
alt=""
src={testimonial.author.imageUrl}
className="size-10 rounded-full bg-gray-50"
/>
<div>
<div className="font-semibold text-gray-900">
{testimonial.author.name}
</div>
<div className="text-gray-600">{`@${testimonial.author.handle}`}</div>
</div>
</figcaption>
</figure>
</a>
</div>
testimonial={testimonial}
/>
))}
</div>
</div>
Expand Down Expand Up @@ -395,31 +312,10 @@ function About() {
<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) => (
<div
<AnimatedTestimonial
key={testimonial.author.handle}
className="pt-8 sm:inline-block sm:w-full sm:px-4"
>
<a target="_blank" href={testimonial.href}>
<figure className="flex flex-col justify-between rounded-2xl bg-gray-50 p-8 text-sm/6">
<blockquote className="flex-1 text-gray-900">
<p className="h-12 overflow-hidden">{`“${testimonial.body}”`}</p>
</blockquote>
<figcaption className="mt-6 flex items-center gap-x-4">
<img
alt=""
src={testimonial.author.imageUrl}
className="size-10 rounded-full bg-gray-50"
/>
<div>
<div className="font-semibold text-gray-900">
{testimonial.author.name}
</div>
<div className="text-gray-600">{`@${testimonial.author.handle}`}</div>
</div>
</figcaption>
</figure>
</a>
</div>
testimonial={testimonial}
/>
))}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import { Disclosure, DisclosureButton } from '@headlessui/react'
import { Bars2Icon } from '@heroicons/react/24/solid'
import { AnimatePresence, motion } from 'framer-motion'
import { Link } from './link'
import { LogoDark } from './logo'
import { Link } from '../link'
import { LogoDark } from '../logo'

const links = [
{ href: '/installation', label: 'Installation' },
Expand Down
114 changes: 114 additions & 0 deletions src/components/about/animated-image-gallery.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { useEffect, useRef } from 'react'

const AnimatedImageGallery = () => {
const imageRefs = [useRef<HTMLDivElement>(null), useRef<HTMLDivElement>(null)]

useEffect(() => {
if (window.innerWidth >= 640) return

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

const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setTimeout(() => {
if (ref.current) {
ref.current.classList.remove(
'opacity-0',
'translate-x-8',
'-translate-x-8',
)
ref.current.classList.add('opacity-100', 'translate-x-0')
}
}, index * 100)
observer.unobserve(entry.target)
}
})
},
{
threshold,
rootMargin: '0px 0px -10% 0px',
},
)

if (ref.current) {
observer.observe(ref.current)
}

return observer
})

return () => {
observers.forEach((observer) => observer.disconnect())
}
}, [])

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>
</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">
<img
alt=""
src="/about/2.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</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"
>
<img
alt=""
src="/about/3.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</div>
</div>
</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">
<img
alt=""
src="/about/4.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</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"
>
<img
alt=""
src="/about/5.webp"
className="aspect-[2/3] w-full rounded-xl bg-gray-900/5 object-cover shadow-lg"
/>
<div className="pointer-events-none absolute inset-0 rounded-xl ring-1 ring-inset ring-gray-900/10" />
</div>
</div>
</div>
</>
)
}

export default AnimatedImageGallery
75 changes: 75 additions & 0 deletions src/components/about/animated-team-members.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useEffect, useRef } from 'react'

interface TeamMember {
name: string
role: string
imageUrl: string
href: string
}

interface AnimatedTeamMemberProps {
person: TeamMember
}

const AnimatedTeamMembers = ({ person }: AnimatedTeamMemberProps) => {
const memberRef = useRef<HTMLLIElement>(null)

useEffect(() => {
if (window.innerWidth >= 640) return

const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (memberRef.current) {
memberRef.current.classList.remove('opacity-0', 'translate-y-8')
memberRef.current.classList.add('opacity-100', 'translate-y-0')
}
observer.unobserve(entry.target)
}
})
},
{
threshold: 0.2,
rootMargin: '0px 0px -10% 0px',
},
)

if (memberRef.current) {
observer.observe(memberRef.current)
}

return () => {
observer.disconnect()
}
}, [])

return (
<li
ref={memberRef}
className="translate-y-8 transform opacity-0 transition-all duration-700 ease-out sm:transform-none sm:opacity-100"
>
<div className="flex items-center gap-x-6">
<div className="group flex items-center gap-x-6">
<a target="_blank" href={person.href}>
<img
alt=""
src={person.imageUrl}
className="size-16 rounded-full"
/>
</a>
<div>
<h3 className="text-base/7 font-semibold tracking-tight text-gray-900">
{person.name}
</h3>
<p className="bg-gradient-to-r from-[#ff8a5b] from-[28%] via-[#fe7e98] via-[70%] to-[#f984ff] bg-clip-text text-sm/6 font-normal text-transparent">
{person.role}
</p>
</div>
</div>
</div>
</li>
)
}

export default AnimatedTeamMembers
Loading

0 comments on commit d052b3c

Please sign in to comment.