Add landing logotype
This commit is contained in:
+2
-14
@@ -1,3 +1,4 @@
|
||||
import { Landing } from "@/app/sections/landing";
|
||||
import { MagneticHoverShowcase } from "@/components/magnetic-hover-showcase";
|
||||
import { ScrollProgressIndicator } from "@/components/scroll-progress-indicator";
|
||||
|
||||
@@ -7,20 +8,7 @@ export default function Home() {
|
||||
<ScrollProgressIndicator />
|
||||
|
||||
<main>
|
||||
<section
|
||||
id="landing"
|
||||
className="flex min-h-screen items-center justify-center px-8 py-16 md:px-16 lg:px-24"
|
||||
>
|
||||
<div className="mx-auto max-w-4xl space-y-8 text-center">
|
||||
<h1 className="font-bold font-heading text-6xl md:text-8xl">Welcome</h1>
|
||||
<p className="font-body text-xl leading-relaxed md:text-2xl">
|
||||
Scroll down to explore the sections and test the progress indicator.
|
||||
</p>
|
||||
<p className="font-cursive text-3xl text-accent-cyan md:text-4xl">
|
||||
Watch the dots on the right...
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<Landing />
|
||||
|
||||
<section id="about" className="min-h-[150vh] px-8 py-16 md:px-16 lg:px-24">
|
||||
<div className="mx-auto max-w-4xl space-y-16">
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import { LandingClient } from "./landing-client";
|
||||
|
||||
const letterSegments: Record<string, [number, number, number, number][]> = {
|
||||
l: [
|
||||
[0, 0, 2, 0],
|
||||
[2, 0, 2, 10],
|
||||
[2, 10, 0, 10],
|
||||
[0, 10, 0, 0],
|
||||
],
|
||||
u: [
|
||||
[0, 3, 0, 10],
|
||||
[0, 10, 4, 10],
|
||||
[4, 10, 4, 3],
|
||||
[4, 3, 0, 3],
|
||||
[2, 3, 2, 8],
|
||||
],
|
||||
i: [
|
||||
[0, 3, 2, 3],
|
||||
[2, 3, 2, 10],
|
||||
[2, 10, 0, 10],
|
||||
[0, 10, 0, 3],
|
||||
[0, 0.5, 2, 0.5],
|
||||
[2, 0.5, 2, 2.5],
|
||||
[2, 2.5, 0, 2.5],
|
||||
[0, 2.5, 0, 0.5],
|
||||
],
|
||||
s: [
|
||||
[0, 3, 5, 3],
|
||||
[0, 10, 5, 10],
|
||||
[0, 3, 0, 7.67],
|
||||
[0, 7.67, 0, 10],
|
||||
[5, 3, 5, 5.33],
|
||||
[5, 5.33, 5, 10],
|
||||
[2, 5.33, 5, 5.33],
|
||||
[0, 7.67, 3, 7.67],
|
||||
],
|
||||
d: [
|
||||
[2, 0, 4, 0],
|
||||
[4, 0, 4, 10],
|
||||
[0, 3, 0, 10],
|
||||
[0, 10, 4, 10],
|
||||
[0, 3, 2, 3],
|
||||
[2, 3, 2, 0],
|
||||
[2, 5, 2, 8],
|
||||
],
|
||||
r: [
|
||||
[0, 3, 4, 3],
|
||||
[2, 5.33, 2, 10],
|
||||
[2, 10, 0, 10],
|
||||
[0, 10, 0, 3],
|
||||
[4, 3, 4, 5.33],
|
||||
[2, 5.33, 4, 5.33],
|
||||
],
|
||||
a: [
|
||||
[0, 3, 0, 5.33],
|
||||
[0, 5.33, 0, 10],
|
||||
[0, 10, 5, 10],
|
||||
[5, 10, 5, 3],
|
||||
[5, 3, 0, 3],
|
||||
[0, 5.33, 3, 5.33],
|
||||
[2, 7.67, 3, 7.67],
|
||||
],
|
||||
v: [
|
||||
[0, 3, 1.5, 10],
|
||||
[5, 3, 3.5, 10],
|
||||
[1.5, 10, 3.5, 10],
|
||||
[2, 3, 2.5, 8],
|
||||
[3, 3, 2.5, 8],
|
||||
[0, 3, 2, 3],
|
||||
[3, 3, 5, 3],
|
||||
],
|
||||
e: [
|
||||
[5, 3, 5, 10],
|
||||
[0, 3, 5, 3],
|
||||
[0, 3, 0, 10],
|
||||
[5, 10, 0, 10],
|
||||
[5, 7.67, 2, 7.67],
|
||||
[2, 5.33, 3, 5.33],
|
||||
],
|
||||
};
|
||||
|
||||
const letterLayout: [string, number][] = [
|
||||
["l", 0],
|
||||
["u", 2.5],
|
||||
["i", 7],
|
||||
["s", 9.5],
|
||||
["d", 15],
|
||||
["r", 19.5],
|
||||
["a", 24],
|
||||
["l", 29.5],
|
||||
["v", 32],
|
||||
["e", 37.5],
|
||||
["s", 43],
|
||||
];
|
||||
|
||||
const allPaths = letterLayout.reduce<{ d: string; key: string }[]>(
|
||||
(paths, [letterKey, xOffset]) => {
|
||||
const letter = letterSegments[letterKey];
|
||||
if (letter) {
|
||||
letter.forEach(([x1, y1, x2, y2]) => {
|
||||
paths.push({
|
||||
d: `M${x1 + xOffset},${y1} L${x2 + xOffset},${y2}`,
|
||||
key: `seg-${paths.length}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
return paths;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
export const Landing = () => {
|
||||
return (
|
||||
<section id="landing">
|
||||
<LandingClient paths={allPaths} />
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { type MotionValue, m, useScroll, useTransform } from "motion/react";
|
||||
import { useRef } from "react";
|
||||
import { springGentle } from "@/lib/motion";
|
||||
|
||||
const STROKE_WIDTH = 0.05;
|
||||
|
||||
const LogotypePath = ({
|
||||
d,
|
||||
scrollProgress,
|
||||
}: {
|
||||
d: string;
|
||||
scrollProgress: MotionValue<number>;
|
||||
}) => {
|
||||
const strokeDashoffset = useTransform(scrollProgress, [0, 0.6], [0, 1]);
|
||||
|
||||
return (
|
||||
<m.path
|
||||
d={d}
|
||||
pathLength={1}
|
||||
strokeDasharray={1}
|
||||
initial={{ strokeDashoffset: 1, opacity: 0 }}
|
||||
animate={{ strokeDashoffset: 0, opacity: 1 }}
|
||||
style={{ strokeDashoffset }}
|
||||
transition={springGentle}
|
||||
data-scroll-animated="stroke-dashoffset:0"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type Props = {
|
||||
paths: { d: string; key: string }[];
|
||||
};
|
||||
|
||||
export const LandingClient = ({ paths }: Props) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
offset: ["start start", "end start"],
|
||||
});
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="flex min-h-screen w-full items-center justify-center">
|
||||
<m.svg
|
||||
role="img"
|
||||
aria-label="luisdralves"
|
||||
viewBox="-0.5 -0.5 49 11"
|
||||
className="w-[90vw] max-w-6xl text-foreground"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={STROKE_WIDTH}
|
||||
strokeLinecap="square"
|
||||
strokeLinejoin="miter"
|
||||
>
|
||||
{paths.map(({ d, key }) => (
|
||||
<LogotypePath key={key} d={d} scrollProgress={scrollYProgress} />
|
||||
))}
|
||||
</m.svg>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -36,6 +36,21 @@ export const RadialReveal = ({ targetSection, origin, onComplete }: Props) => {
|
||||
clone.style.transform = `translateY(-${clampedScrollY}px)`;
|
||||
clone.style.pointerEvents = "none";
|
||||
|
||||
// Reset scroll-animated elements to their initial visual state
|
||||
// Format: data-scroll-animated="property1:value1;property2:value2"
|
||||
clone.querySelectorAll("[data-scroll-animated]").forEach((el) => {
|
||||
const htmlEl = el as HTMLElement | SVGElement;
|
||||
const resetValues = htmlEl.dataset.scrollAnimated;
|
||||
if (resetValues) {
|
||||
resetValues.split(";").forEach((pair) => {
|
||||
const [property, value] = pair.split(":");
|
||||
if (property && value !== undefined) {
|
||||
htmlEl.style.setProperty(property, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
overlayRef.current.innerHTML = "";
|
||||
overlayRef.current.appendChild(clone);
|
||||
setIsReady(true);
|
||||
|
||||
Reference in New Issue
Block a user