add animated headings
This commit is contained in:
parent
9676ff9327
commit
1fd4d1cbbe
17
web/src/components/AnimateHeadings.astro
Normal file
17
web/src/components/AnimateHeadings.astro
Normal 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>
|
@ -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>
|
||||
)
|
||||
|
@ -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 />
|
||||
|
37
web/src/lib/animate-heading.ts
Normal file
37
web/src/lib/animate-heading.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -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] },
|
||||
|
@ -140,3 +140,7 @@ li {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.letter {
|
||||
display: inline-block;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user