From e88aa5f3b5df30132566ef1a779ca2f51225b069 Mon Sep 17 00:00:00 2001 From: "zhangxu.709" Date: Mon, 25 May 2026 21:07:48 +0800 Subject: [PATCH] feat: implement scroll fade animations and reduced motion support for improved user experience. --- src/assets/styles/tailwind.css | 50 ++++++++++++++++++++++++ src/components/common/BasicScripts.astro | 44 +++++++++++++++++++++ src/layouts/Layout.astro | 3 ++ src/pages/index.astro | 12 +++--- src/pages/publications.astro | 2 +- src/pages/supported-models.astro | 4 +- src/pages/team.astro | 4 +- 7 files changed, 108 insertions(+), 11 deletions(-) diff --git a/src/assets/styles/tailwind.css b/src/assets/styles/tailwind.css index 0cf2ccc..80dff69 100644 --- a/src/assets/styles/tailwind.css +++ b/src/assets/styles/tailwind.css @@ -42,6 +42,56 @@ } } +@media (prefers-reduced-motion: no-preference) { + body { + animation: page-fade-in 480ms cubic-bezier(0.22, 1, 0.36, 1) both; + } + + .js-enabled .scroll-fade, + .js-enabled .scroll-fade-stagger > * { + opacity: 0; + translate: 0 1.75rem; + transition: + opacity 680ms cubic-bezier(0.22, 1, 0.36, 1), + translate 680ms cubic-bezier(0.22, 1, 0.36, 1); + transition-delay: var(--scroll-fade-delay, 0ms); + will-change: opacity, translate; + } + + .js-enabled .scroll-fade.is-visible, + .js-enabled .scroll-fade-stagger.is-visible > * { + opacity: 1; + translate: 0 0; + } + + .js-enabled .scroll-fade:not(.is-visible), + .js-enabled .scroll-fade-stagger:not(.is-visible) > * { + transition-delay: 0ms; + } +} + +@media (prefers-reduced-motion: reduce) { + body { + animation: none; + } + + .scroll-fade, + .scroll-fade-stagger > * { + opacity: 1; + translate: 0 0; + transition: none; + } +} + +@keyframes page-fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + #header.scroll > div:first-child { @apply bg-page md:bg-white/90 md:backdrop-blur-md; box-shadow: 0 0.375rem 1.5rem 0 rgb(140 152 164 / 13%); diff --git a/src/components/common/BasicScripts.astro b/src/components/common/BasicScripts.astro index e84c79b..e03778a 100644 --- a/src/components/common/BasicScripts.astro +++ b/src/components/common/BasicScripts.astro @@ -143,6 +143,7 @@ import { UI } from 'astrowind:config'; }); }; const onPageShow = function () { + document.documentElement.classList.add('js-enabled'); document.documentElement.classList.add('motion-safe:scroll-smooth'); const elem = document.querySelector('[data-aw-toggle-menu]'); if (elem) { @@ -266,9 +267,52 @@ import { UI } from 'astrowind:config'; }, }; + const PageMotion = { + observer: null, + + start() { + document.documentElement.classList.add('js-enabled'); + this.observer?.disconnect(); + + const elements = Array.from(document.querySelectorAll('.scroll-fade, .scroll-fade-stagger')); + const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + elements.forEach((element) => { + if (element.classList.contains('scroll-fade-stagger')) { + Array.from(element.children).forEach((child, index) => { + child.style.setProperty('--scroll-fade-delay', `${Math.min(index, 6) * 90}ms`); + }); + } + + if (prefersReducedMotion) { + element.classList.add('is-visible'); + } + }); + + if (prefersReducedMotion || !elements.length) { + return; + } + + this.observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + entry.target.classList.toggle('is-visible', entry.isIntersecting); + }); + }, + { threshold: 0.12, rootMargin: '0px 0px -10% 0px' } + ); + + elements.forEach((element) => { + this.observer.observe(element); + }); + }, + }; + Observer.start(); + PageMotion.start(); document.addEventListener('astro:after-swap', () => { Observer.start(); + PageMotion.start(); }); diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 524a26f..a4bc609 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -29,6 +29,9 @@ const { language, textDirection } = I18N; + diff --git a/src/pages/index.astro b/src/pages/index.astro index d65af53..9da9702 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -26,7 +26,7 @@ const metadata = {