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 { HTMLAttributes } from "astro/types";
|
||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { type VariantProps, cva } from "class-variance-authority";
|
||||||
|
|
||||||
const heading = cva("max-w-3xl m-[var(--section-heading-margin)]", {
|
const heading = cva(
|
||||||
variants: {
|
"max-w-3xl m-[var(--section-heading-margin)] overflow-hidden",
|
||||||
main: {
|
{
|
||||||
true: "font-bold",
|
variants: {
|
||||||
false: "font-semibold",
|
main: {
|
||||||
},
|
true: "font-bold animated-heading",
|
||||||
padding: {
|
false: "font-semibold",
|
||||||
true: "py-4",
|
},
|
||||||
false: "p-0",
|
animated: {
|
||||||
|
true: "animated-heading",
|
||||||
|
false: "",
|
||||||
|
},
|
||||||
|
padding: {
|
||||||
|
true: "py-4",
|
||||||
|
false: "p-0",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
export interface Props
|
export interface Props
|
||||||
extends HTMLAttributes<"h1" | "h2">,
|
extends HTMLAttributes<"h1" | "h2">,
|
||||||
@ -22,22 +29,28 @@ export interface Props
|
|||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { main = false, padding = true, subtitle } = Astro.props;
|
const {
|
||||||
|
main = false,
|
||||||
|
padding = true,
|
||||||
|
subtitle,
|
||||||
|
id,
|
||||||
|
class: propsClass,
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
const titleClass = cn(
|
const titleClass = cn(
|
||||||
heading({ main, padding }),
|
heading({ main, padding, class: propsClass }),
|
||||||
subtitle ? "mb-[calc(var(--section_heading-margin-bottom)_/_2)]" : ""
|
subtitle ? "mb-[calc(var(--section_heading-margin-bottom)_/_2)]" : "",
|
||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="z-0 relative">
|
<div class="z-0 relative">
|
||||||
{
|
{
|
||||||
main ? (
|
main ? (
|
||||||
<h1 class={titleClass}>
|
<h1 id={id} class={titleClass}>
|
||||||
<slot />
|
<slot />
|
||||||
</h1>
|
</h1>
|
||||||
) : (
|
) : (
|
||||||
<h2 class={titleClass}>
|
<h2 id={id} class={titleClass}>
|
||||||
<slot />
|
<slot />
|
||||||
</h2>
|
</h2>
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ import Header from "@/components/Layout/Header.astro";
|
|||||||
import "@/styles/global.css";
|
import "@/styles/global.css";
|
||||||
import "keen-slider/keen-slider.min.css";
|
import "keen-slider/keen-slider.min.css";
|
||||||
import { ViewTransitions } from "astro:transitions";
|
import { ViewTransitions } from "astro:transitions";
|
||||||
|
import AnimateHeadings from "@/components/AnimateHeadings.astro";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@ -27,6 +28,7 @@ const { title } = Astro.props;
|
|||||||
<meta name="generator" content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<title>vspace.one - {title}</title>
|
<title>vspace.one - {title}</title>
|
||||||
<ViewTransitions />
|
<ViewTransitions />
|
||||||
|
<AnimateHeadings />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<Header />
|
<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 "pannellum/build/pannellum.css";
|
||||||
import pano from "../../public/pic/panorama/maschinenraum_half.jpg";
|
import pano from "../../public/pic/panorama/maschinenraum_half.jpg";
|
||||||
import { Image } from "astro:assets";
|
import { Image } from "astro:assets";
|
||||||
|
import Heading from "@/components/Heading.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="h-[60vh] w-full flex justify-center items-center">
|
<div class="h-[60vh] w-full flex justify-center items-center">
|
||||||
@ -15,7 +16,14 @@ import { Image } from "astro:assets";
|
|||||||
max-md:w-[90vw]"
|
max-md:w-[90vw]"
|
||||||
>
|
>
|
||||||
<!--h1>vspace.one</h1-->
|
<!--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>
|
<p>
|
||||||
<a class="text-xl" href="/tour">Hier gehts zur virtuellen Space-Tour!</a
|
<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 { animate } from "motion";
|
||||||
import { ease } from "@unom/style";
|
import { ease } from "@unom/style";
|
||||||
import "pannellum";
|
import "pannellum";
|
||||||
|
import animateHeading from "@/lib/animate-heading";
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
const panoBg = document.getElementById("pano-bg")!;
|
const panoBg = document.getElementById("pano-bg")!;
|
||||||
const panoImg = document.getElementById("pano-img")! as HTMLImageElement;
|
const panoImg = document.getElementById("pano-img")! as HTMLImageElement;
|
||||||
|
const headingMakerspace = document.getElementById("heading-makerspace")!;
|
||||||
|
|
||||||
const viewer = pannellum.viewer(panoBg, {
|
const viewer = pannellum.viewer(panoBg, {
|
||||||
panorama: panoImg.src,
|
panorama: panoImg.src,
|
||||||
@ -63,6 +73,8 @@ import { Image } from "astro:assets";
|
|||||||
viewer.on("load", () => {
|
viewer.on("load", () => {
|
||||||
console.log("loaded");
|
console.log("loaded");
|
||||||
|
|
||||||
|
animateHeading(headingMakerspace, 0.5);
|
||||||
|
|
||||||
animate(
|
animate(
|
||||||
panoBg,
|
panoBg,
|
||||||
{ scale: [0.9, 1], opacity: [0, 1] },
|
{ scale: [0.9, 1], opacity: [0, 1] },
|
||||||
|
@ -139,4 +139,8 @@ li {
|
|||||||
to {
|
to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter {
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user