add animated headings

This commit is contained in:
Enrico Bühler 2024-07-14 14:12:22 +02:00
parent 9676ff9327
commit 1fd4d1cbbe
6 changed files with 101 additions and 16 deletions

View File

@ -0,0 +1,17 @@
<script>
import animateHeading from "@/lib/animate-heading";
const init = () => {
const headings = document.querySelectorAll(".animated-heading");
headings.forEach((e) => {
animateHeading(e);
});
};
init();
document.addEventListener("astro:after-swap", () => {
init();
});
</script>

View File

@ -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)]" : "",
);
---
<div class="z-0 relative">
{
main ? (
<h1 class={titleClass}>
<h1 id={id} class={titleClass}>
<slot />
</h1>
) : (
<h2 class={titleClass}>
<h2 id={id} class={titleClass}>
<slot />
</h2>
)

View File

@ -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;
<meta name="generator" content={Astro.generator} />
<title>vspace.one - {title}</title>
<ViewTransitions />
<AnimateHeadings />
</head>
<body>
<Header />

View File

@ -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,
"<span class='letter'>$&</span>",
);
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);
}
}

View File

@ -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";
---
<div class="h-[60vh] w-full flex justify-center items-center">
@ -15,7 +16,14 @@ import { Image } from "astro:assets";
max-md:w-[90vw]"
>
<!--h1>vspace.one</h1-->
<h2 class="text-4xl text-center">Ein Makerspace und Hackerspace</h2>
<Heading
padding={false}
animated
id="heading-makerspace"
class="text-4xl text-center"
>
Ein Makerspace und Hackerspace
</Heading>
<p>
<a class="text-xl" href="/tour">Hier gehts zur virtuellen Space-Tour!</a
>
@ -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] },

View File

@ -139,4 +139,8 @@ li {
to {
opacity: 0;
}
}
.letter {
display: inline-block;
}