From 1fd4d1cbbe9dc3192f5c79566e16fa54481b4ec3 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sun, 14 Jul 2024 14:12:22 +0200 Subject: [PATCH] add animated headings --- web/src/components/AnimateHeadings.astro | 17 ++++++++++ web/src/components/Heading.astro | 43 +++++++++++++++--------- web/src/layouts/RootLayout.astro | 2 ++ web/src/lib/animate-heading.ts | 37 ++++++++++++++++++++ web/src/sections/LandingHeader.astro | 14 +++++++- web/src/styles/global.css | 4 +++ 6 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 web/src/components/AnimateHeadings.astro create mode 100644 web/src/lib/animate-heading.ts diff --git a/web/src/components/AnimateHeadings.astro b/web/src/components/AnimateHeadings.astro new file mode 100644 index 0000000..1886a5c --- /dev/null +++ b/web/src/components/AnimateHeadings.astro @@ -0,0 +1,17 @@ + diff --git a/web/src/components/Heading.astro b/web/src/components/Heading.astro index d789f1c..fac64af 100644 --- a/web/src/components/Heading.astro +++ b/web/src/components/Heading.astro @@ -3,18 +3,25 @@ import { cn } from "@/lib/utils"; import type { HTMLAttributes } from "astro/types"; import { type VariantProps, cva } from "class-variance-authority"; -const heading = cva("max-w-3xl m-[var(--section-heading-margin)]", { - variants: { - main: { - true: "font-bold", - false: "font-semibold", - }, - padding: { - true: "py-4", - false: "p-0", +const heading = cva( + "max-w-3xl m-[var(--section-heading-margin)] overflow-hidden", + { + variants: { + main: { + true: "font-bold animated-heading", + false: "font-semibold", + }, + animated: { + true: "animated-heading", + false: "", + }, + padding: { + true: "py-4", + false: "p-0", + }, }, }, -}); +); export interface Props extends HTMLAttributes<"h1" | "h2">, @@ -22,22 +29,28 @@ export interface Props subtitle?: string; } -const { main = false, padding = true, subtitle } = Astro.props; +const { + main = false, + padding = true, + subtitle, + id, + class: propsClass, +} = Astro.props; const titleClass = cn( - heading({ main, padding }), - subtitle ? "mb-[calc(var(--section_heading-margin-bottom)_/_2)]" : "" + heading({ main, padding, class: propsClass }), + subtitle ? "mb-[calc(var(--section_heading-margin-bottom)_/_2)]" : "", ); ---
{ main ? ( -

+

) : ( -

+

) diff --git a/web/src/layouts/RootLayout.astro b/web/src/layouts/RootLayout.astro index 5f4d595..0d5755c 100644 --- a/web/src/layouts/RootLayout.astro +++ b/web/src/layouts/RootLayout.astro @@ -4,6 +4,7 @@ import Header from "@/components/Layout/Header.astro"; import "@/styles/global.css"; import "keen-slider/keen-slider.min.css"; import { ViewTransitions } from "astro:transitions"; +import AnimateHeadings from "@/components/AnimateHeadings.astro"; export interface Props { title: string; @@ -27,6 +28,7 @@ const { title } = Astro.props; vspace.one - {title} +
diff --git a/web/src/lib/animate-heading.ts b/web/src/lib/animate-heading.ts new file mode 100644 index 0000000..97c861c --- /dev/null +++ b/web/src/lib/animate-heading.ts @@ -0,0 +1,37 @@ +import { animate, spring, stagger } from "motion"; + +export default function animateHeading(e: Element, delay?: number) { + const id = Math.floor(Math.random() * 100).toString(); + + e.id = id; + + if (e.textContent) { + e.innerHTML = e.textContent.replace( + /\S/g, + "$&", + ); + + const updatedElement = document.getElementById(id)!; + + console.log("updated", updatedElement); + + const letters = updatedElement.querySelectorAll(`:scope > .letter`); + + const observer = new IntersectionObserver( + () => { + console.log("inview"); + animate( + letters, + { y: [100, 0] }, + { + easing: spring({ stiffness: 200, damping: 20 }), + delay: stagger(0.02, { start: delay !== undefined ? delay : 0 }), + }, + ); + }, + { threshold: 0.01 }, + ); + + observer.observe(e); + } +} \ No newline at end of file diff --git a/web/src/sections/LandingHeader.astro b/web/src/sections/LandingHeader.astro index 722149f..b2267bf 100644 --- a/web/src/sections/LandingHeader.astro +++ b/web/src/sections/LandingHeader.astro @@ -2,6 +2,7 @@ import "pannellum/build/pannellum.css"; import pano from "../../public/pic/panorama/maschinenraum_half.jpg"; import { Image } from "astro:assets"; +import Heading from "@/components/Heading.astro"; ---
@@ -15,7 +16,14 @@ import { Image } from "astro:assets"; max-md:w-[90vw]" > -

Ein Makerspace und Hackerspace

+ + Ein Makerspace und Hackerspace +

Hier gehts zur virtuellen Space-Tour! @@ -42,10 +50,12 @@ import { Image } from "astro:assets"; import { animate } from "motion"; import { ease } from "@unom/style"; import "pannellum"; + import animateHeading from "@/lib/animate-heading"; const init = () => { const panoBg = document.getElementById("pano-bg")!; const panoImg = document.getElementById("pano-img")! as HTMLImageElement; + const headingMakerspace = document.getElementById("heading-makerspace")!; const viewer = pannellum.viewer(panoBg, { panorama: panoImg.src, @@ -63,6 +73,8 @@ import { Image } from "astro:assets"; viewer.on("load", () => { console.log("loaded"); + animateHeading(headingMakerspace, 0.5); + animate( panoBg, { scale: [0.9, 1], opacity: [0, 1] }, diff --git a/web/src/styles/global.css b/web/src/styles/global.css index b911e54..a188271 100644 --- a/web/src/styles/global.css +++ b/web/src/styles/global.css @@ -139,4 +139,8 @@ li { to { opacity: 0; } +} + +.letter { + display: inline-block; } \ No newline at end of file