WIP: Astrowind build with scroll animation in progress

This commit is contained in:
findingfrankie 2026-01-19 01:45:05 -05:00
parent 8efe79c42e
commit 06443bc67c
13 changed files with 224 additions and 33 deletions

51
package-lock.json generated
View File

@ -16,6 +16,7 @@
"astro": "^5.16.7",
"astro-embed": "^0.9.0",
"astro-icon": "^1.1.5",
"gsap": "^3.14.2",
"limax": "4.1.0",
"lodash.merge": "^4.6.2",
"unpic": "^4.1.3"
@ -3192,9 +3193,9 @@
}
},
"node_modules/astro": {
"version": "5.16.7",
"resolved": "https://registry.npmjs.org/astro/-/astro-5.16.7.tgz",
"integrity": "sha512-Kfv7FKisFR+THvmojXWtvJGRCvQ4D9przguE9XdeUtS464ned6hvbgmyFDvPzyaNmDtkHGNpPwAQ9tgFcVqp+Q==",
"version": "5.16.11",
"resolved": "https://registry.npmjs.org/astro/-/astro-5.16.11.tgz",
"integrity": "sha512-Z7kvkTTT5n6Hn5lCm6T3WU6pkxx84Hn25dtQ6dR7ATrBGq9eVa8EuB/h1S8xvaoVyCMZnIESu99Z9RJfdLRLDA==",
"license": "MIT",
"peer": true,
"dependencies": {
@ -3216,8 +3217,8 @@
"cssesc": "^3.0.0",
"debug": "^4.4.3",
"deterministic-object-hash": "^2.0.2",
"devalue": "^5.6.1",
"diff": "^5.2.0",
"devalue": "^5.6.2",
"diff": "^8.0.3",
"dlv": "^1.1.3",
"dset": "^3.1.4",
"es-module-lexer": "^1.7.0",
@ -4735,9 +4736,9 @@
}
},
"node_modules/devalue": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.1.tgz",
"integrity": "sha512-jDwizj+IlEZBunHcOuuFVBnIMPAEHvTsJj0BcIp94xYguLRVBcXO853px/MyIJvbVzWdsGvrRweIUWJw8hBP7A==",
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz",
"integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==",
"license": "MIT"
},
"node_modules/devlop": {
@ -4761,9 +4762,9 @@
"license": "Apache-2.0"
},
"node_modules/diff": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
"integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
@ -5805,10 +5806,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/gsap": {
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz",
"integrity": "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==",
"license": "Standard 'no charge' license: https://gsap.com/standard-license."
},
"node_modules/h3": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.4.tgz",
"integrity": "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==",
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
"integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
"license": "MIT",
"dependencies": {
"cookie-es": "^1.2.2",
@ -5816,9 +5823,9 @@
"defu": "^6.1.4",
"destr": "^2.0.5",
"iron-webcrypto": "^1.2.1",
"node-mock-http": "^1.0.2",
"node-mock-http": "^1.0.4",
"radix3": "^1.1.2",
"ufo": "^1.6.1",
"ufo": "^1.6.3",
"uncrypto": "^0.1.3"
}
},
@ -10079,9 +10086,9 @@
}
},
"node_modules/tar": {
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz",
"integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==",
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.3.tgz",
"integrity": "sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
@ -10344,9 +10351,9 @@
}
},
"node_modules/ufo": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.2.tgz",
"integrity": "sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q==",
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
"integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
"license": "MIT"
},
"node_modules/uhyphen": {

View File

@ -30,6 +30,7 @@
"astro": "^5.16.7",
"astro-embed": "^0.9.0",
"astro-icon": "^1.1.5",
"gsap": "^3.14.2",
"limax": "4.1.0",
"lodash.merge": "^4.6.2",
"unpic": "^4.1.3"

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -46,11 +46,7 @@ const socialLinks = [
</a>
))}
</div>
<!-- Tagline - Left Aligned -->
<div class="text-left text-white/50 text-sm">
Laconic: Offshore Hosting Made Easy
</div>
</div>
</footer>

View File

@ -0,0 +1,183 @@
---
// ScrollBallAnimation.astro
// Scroll-triggered animation with glowing ball traveling along SVG path between wireframe hands
interface Props {
startTrigger?: string; // Default: "#watchers"
endTrigger?: string; // Default: "#deploy"
}
const {
startTrigger = '#watchers',
endTrigger = '#indexer',
} = Astro.props;
---
<!-- Wrapper: Creates positioning context at insertion point -->
<div class="animation-wrapper relative w-full pointer-events-none" style="height: 0;">
<!-- Background hands: Positioned behind text content but above page background -->
<div class="hands-background absolute left-0 right-0 pointer-events-none" style="top: 0; height: 250vh; z-index: 1;">
<div class="background-image absolute inset-0 w-full h-full"></div>
</div>
<!-- Ball and path container: Above background hands -->
<div class="ball-container absolute left-0 right-0 hidden md:block" style="top: 0; height: 250vh; z-index: 3;">
<!-- SVG path for debugging (visible red line) -->
<svg class="path-svg absolute inset-0 w-full h-full" viewBox="0 0 1920 1080" preserveAspectRatio="xMidYMid meet">
<path
id="ballLine"
d="M 480,-80 C 480,0 600,250 960,280 C 1320,310 1440,400 1440,480"
stroke="red"
stroke-width="3"
fill="none" />
</svg>
<!-- The glowing ball -->
<div id="scrollBall" class="ball"></div>
</div>
</div>
<style>
.hands-background {
pointer-events: none;
}
.ball-container {
pointer-events: none;
}
/* Ball: 220px circle with multi-layer blue glow effect */
.ball {
width: 220px;
height: 220px;
border-radius: 50%;
background: white;
position: absolute;
top: 0;
left: 0;
mix-blend-mode: screen; /* Creates glowing effect against dark background */
opacity: 0.85;
/* Multi-layer box-shadow for realistic glow */
box-shadow:
0px 4px 115px 15px #0000f4, /* Outer blue glow */
0px 0px 70px 20px #0a33ff, /* Middle glow */
inset -39px -20px 100px rgba(0, 0, 244, 0.73); /* Inner gradient for depth */
/* GPU acceleration for smooth animation */
will-change: transform;
transform: translateZ(0);
}
/* Hide entire animation on mobile for performance */
@media (max-width: 768px) {
.ball,
.ball-container,
.hands-background {
display: none !important;
}
}
/* Background image: wireframe hands, theme-aware */
.background-image {
background-image: url('/images/benefit-bg.png'); /* Dark theme (default) */
background-size: contain;
background-position: center top;
background-repeat: no-repeat;
min-height: 100%;
border: 2px solid lime; /* Debug: make container visible */
}
/* Light theme background variant */
:root:not(.dark) .background-image {
background-image: url('/images/benefit-bg-light.png');
}
.path-svg {
opacity: 1; /* Visible for debugging */
}
</style>
<script>
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { MotionPathPlugin } from 'gsap/MotionPathPlugin';
// Register GSAP plugins
gsap.registerPlugin(ScrollTrigger, MotionPathPlugin);
function initBallAnimation() {
// Get DOM elements
const ball = document.querySelector('#scrollBall');
const watchersHeading = document.querySelector('#watchers h3');
const indexerHeading = document.querySelector('#indexer h3');
// Safety check: ensure all required elements exist
if (!ball || !watchersHeading || !indexerHeading) {
console.warn('Ball animation: Required elements not found');
return;
}
// Don't run animation on mobile devices
if (window.matchMedia('(max-width: 768px)').matches) {
return;
}
// Kill any existing ScrollTrigger instances
ScrollTrigger.getAll().forEach(st => {
if (st.vars.id === 'ball-animation') {
st.kill();
}
});
// Hide ball initially
gsap.set(ball, { opacity: 0 });
// Create GSAP timeline with ScrollTrigger
const timeline = gsap.timeline({
scrollTrigger: {
id: 'ball-animation',
trigger: watchersHeading,
start: 'bottom top', // Start when watchers H3 bottom leaves screen (hits top)
endTrigger: indexerHeading,
end: 'top center', // End when indexer H3 hits center of viewport
scrub: 1,
markers: true,
onEnter: () => gsap.set(ball, { opacity: 0.85 }),
onLeaveBack: () => gsap.set(ball, { opacity: 0 }),
}
});
// Animate ball along SVG path
timeline.to(ball, {
motionPath: {
path: '#ballLine',
align: '#ballLine',
alignOrigin: [0.5, 0.5],
autoRotate: false,
},
ease: 'none',
duration: 1,
});
}
// Initialize animation when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initBallAnimation);
} else {
initBallAnimation();
}
// Reinitialize on Astro View Transitions
document.addEventListener('astro:page-load', initBallAnimation);
// Cleanup on navigation
document.addEventListener('astro:before-preparation', () => {
ScrollTrigger.getAll().forEach(st => {
if (st.vars.id === 'ball-animation') {
st.kill();
}
});
});
</script>

View File

@ -15,7 +15,7 @@ const { metadata } = Astro.props;
---
<Layout metadata={metadata}>
{/* Announcement removed - not needed for Laconic site */}
<slot name="header">
<Header />

View File

@ -4,6 +4,7 @@ import VideoHero from '~/components/widgets/VideoHero.astro';
import TextSection from '~/components/widgets/TextSection.astro';
import ButtonWhiteOutline from '~/components/widgets/ButtonWhiteOutline.astro';
import ButtonBlue from '~/components/widgets/ButtonBlue.astro';
import ScrollBallAnimation from '~/components/widgets/ScrollBallAnimation.astro';
const metadata = {
title: 'Laconic Network',
@ -26,7 +27,7 @@ const metadata = {
<!-- 2. INTRO SECTION -->
<TextSection bgColor="bg-dark" maxWidth="max-w-4xl">
<div id="infrastructure" class="scroll-mt-20 py-20">
<div id="infrastructure" class="scroll-mt-20 py-20 relative z-10">
<p class="text-lg md:text-xl mb-12 leading-relaxed text-white/90">
Laconic is launching our flagship datacenter to participate in the Solana network.
Host or co-locate your onchain business with Laconic to streamline development and
@ -51,16 +52,19 @@ const metadata = {
<!-- 3. PRODUCTS INTRO -->
<TextSection bgColor="bg-dark" maxWidth="max-w-5xl">
<div class="scroll-mt-20 py-20">
<div id="products-intro" class="scroll-mt-20 py-20 relative z-10">
<h2 class="font-display text-4xl md:text-6xl mb-16 text-center text-white leading-tight">
Monitor, Index, and Deploy with<br />Laconic's Premier Product Suite
</h2>
</div>
</TextSection>
<!-- Scroll Ball Animation: Glowing ball traveling between wireframe hands -->
<ScrollBallAnimation startTrigger="#watchers" endTrigger="#indexer" />
<!-- 4. WATCHERS SECTION -->
<TextSection bgColor="bg-dark" maxWidth="max-w-4xl">
<div id="watchers" class="scroll-mt-20 py-20">
<div id="watchers" class="scroll-mt-20 py-20 relative z-10">
<h3 class="font-display text-3xl md:text-5xl mb-6 text-white">
Watchers
</h3>
@ -98,7 +102,7 @@ const metadata = {
<!-- 5. INDEXER SECTION -->
<TextSection bgColor="bg-dark" maxWidth="max-w-4xl">
<div id="indexer" class="scroll-mt-20 py-20">
<div id="indexer" class="scroll-mt-20 py-20 relative z-10">
<h3 class="font-display text-3xl md:text-5xl mb-6 text-white">
Indexer
</h3>
@ -136,7 +140,7 @@ const metadata = {
<!-- 6. LACONIC DEPLOY SECTION -->
<TextSection bgColor="bg-dark" maxWidth="max-w-4xl">
<div id="deploy" class="scroll-mt-20 py-20">
<div id="deploy" class="scroll-mt-20 py-20 relative z-10">
<h3 class="font-display text-3xl md:text-5xl mb-6 text-white">
Laconic Deploy
</h3>