feat: add fade up animation to post page, optimize global animation curves and durations, refine animation-related naming conventions

This commit is contained in:
radishzzz 2025-05-17 20:18:41 +01:00
parent d4ea4c470b
commit a35999629a
14 changed files with 122 additions and 52 deletions

8
pnpm-lock.yaml generated
View file

@ -32,6 +32,9 @@ importers:
feed:
specifier: ^5.0.1
version: 5.0.1
gsap:
specifier: ^3.13.0
version: 3.13.0
markdown-it:
specifier: ^14.1.0
version: 14.1.0
@ -2191,6 +2194,9 @@ packages:
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
gsap@3.13.0:
resolution: {integrity: sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==}
gzip-size@6.0.0:
resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
engines: {node: '>=10'}
@ -6284,6 +6290,8 @@ snapshots:
graphemer@1.4.0: {}
gsap@3.13.0: {}
gzip-size@6.0.0:
dependencies:
duplexer: 0.1.2

View file

@ -93,15 +93,15 @@ function setupThemeToggle() {
}
// Temporarily add markers during animation to implement view transition and disable CSS transitions
document.documentElement.style.setProperty('view-transition-name', 'theme-transition')
document.documentElement.setAttribute('data-theme-transition', '')
document.documentElement.style.setProperty('view-transition-name', 'animation-theme-toggle')
document.documentElement.setAttribute('data-theme-changing', '')
// If browser supports View Transitions API, use it to update theme
const themeTransition = document.startViewTransition(updateTheme)
// Remove markers after animation
themeTransition.finished.then(() => {
document.documentElement.style.removeProperty('view-transition-name')
document.documentElement.removeAttribute('data-theme-transition')
document.documentElement.removeAttribute('data-theme-changing')
})
})
})

View file

@ -32,7 +32,7 @@ const SubtitleTag = isPost ? 'div' : 'h2'
<div
class="box-content inline-block pr-1"
transition:name={`site-title-${currentLang}`}
data-disable-transition-on-theme
data-disable-theme-transition
>
<a
id="site-title-link"

View file

@ -43,7 +43,7 @@ function getPostPath(post: Post) {
lg={isHome ? 'font-medium text-4.5' : ''}
href={getPostPath(post)}
transition:name={`post-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme
data-disable-theme-transition
>
{post.data.title}
</a>
@ -62,7 +62,7 @@ function getPostPath(post: Post) {
<div
class="py-0.8 text-3.5 font-time lg:hidden"
transition:name={`time-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme
data-disable-theme-transition
>
<PostDate
date={post.data.published}

View file

@ -0,0 +1,18 @@
<script>
function resetFadeUpAnimation() {
document.querySelectorAll('.animation-fade-up').forEach((container) => {
const childElements = Array.from(container.children).slice(0, 20)
childElements.forEach(element =>
(element as HTMLElement).style.animationName = 'none',
)
requestAnimationFrame(() => {
childElements.forEach(element =>
(element as HTMLElement).style.animationName = '',
)
})
})
}
resetFadeUpAnimation()
document.addEventListener('astro:page-load', resetFadeUpAnimation)
</script>

View file

@ -5,7 +5,7 @@ import GoBackIcon from '@/assets/icons/go-back.svg';
<button
id="back-button"
class="hidden"
lg="block absolute c-secondary/40 left--10 top-1/2 aspect-square w-4.5 translate-y--1/2 transition-colors c-secondary active:scale-90 hover:c-primary/80"
lg="block absolute c-secondary/40 left--10 top-1/2 aspect-square w-4.5 translate-y--1/2 transition-colors duration-300 ease-out c-secondary active:scale-90 hover:c-primary/80"
aria-label="Go back"
>
<GoBackIcon

View file

@ -29,7 +29,7 @@ const filteredHeadings = headings.filter(heading =>
<div class="relative h-12 w-full">
<label
for="toc-toggle"
class="absolute inset-0 flex cursor-pointer items-center 2xl:(static flex c-secondary/40 transition-colors hover:c-secondary/80)"
class="absolute inset-0 flex cursor-pointer items-center 2xl:(static flex c-secondary/40 transition-colors duration-300 ease-out hover:c-secondary/80)"
>
<span class="toc-title">
{currentUI.toc}

View file

@ -3,11 +3,13 @@ import Button from '@/components/Button.astro'
import Footer from '@/components/Footer.astro'
import Header from '@/components/Header.astro'
import Navbar from '@/components/Navbar.astro'
import FadeUpAnimation from '@/components/Widgets/FadeUpAnimation.astro'
import GithubCard from '@/components/Widgets/GithubCard.astro'
// import PhotoSwipe from '@/components/Widgets/PhotoSwipe.astro'
import PhotoSwipe from '@/components/Widgets/PhotoSwipe.astro'
import themeConfig from '@/config'
import Head from '@/layouts/Head.astro'
import { getPageInfo } from '@/utils/page'
import '@/styles/animation.css'
import '@/styles/global.css'
import '@/styles/font.css'
import '@/styles/heti.css'
@ -46,8 +48,9 @@ const MarginBottom = isPost && themeConfig.comment?.enabled
</main>
<Footer />
</div>
<FadeUpAnimation />
<Button supportedLangs={supportedLangs} />
<GithubCard />
<!-- <PhotoSwipe /> -->
<PhotoSwipe />
</body>
</html>

View file

@ -114,7 +114,7 @@ const { Content, headings, remarkPluginFrontmatter } = await render(post)
<h1 class="post-title">
<span
transition:name={`post-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme
data-disable-theme-transition
>
{post.data.title}
</span>
@ -125,7 +125,7 @@ const { Content, headings, remarkPluginFrontmatter } = await render(post)
<div
class="mb-16.3 block c-primary font-time"
transition:name={`time-${post.data.abbrlink || post.id}-${lang}`}
data-disable-transition-on-theme
data-disable-theme-transition
>
<PostDate
date={post.data.published}
@ -136,7 +136,9 @@ const { Content, headings, remarkPluginFrontmatter } = await render(post)
<!-- TOC -->
{post.data.toc && <TOC headings={headings} />}
<!-- Content -->
<Content />
<div class="animation-fade-up">
<Content />
</div>
</article>
<!-- Tags -->

74
src/styles/animation.css Normal file
View file

@ -0,0 +1,74 @@
/* View Transition */
::view-transition-new(animation-theme-toggle) {
animation: reveal 1s cubic-bezier(0.4, 0, 0.2, 1);
clip-path: inset(0 0 0 0);
z-index: 99;
}
::view-transition-old(animation-theme-toggle) {
animation: none;
z-index: -1;
}
@keyframes reveal {
from {
clip-path: inset(var(--from));
}
}
html.dark {
--from: 0 0 100% 0;
}
html:not(.dark) {
--from: 100% 0 0 0;
}
/* Disable animations for special elements during theme switching */
html[data-theme-changing] [data-disable-theme-transition] {
view-transition-name: none !important;
}
/* Fallback animation when view-transition-name is not supported */
@supports not (view-transition-name: none) {
html {
--at-apply: 'transition-colors duration-300 ease-out';
}
}
/* Fade Up Animation >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(3rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animation-fade-up > * {
opacity: 0;
}
.animation-fade-up > *:nth-child(-n+20) {
animation: fadeUp 1.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
animation-delay: calc((var(--animation-order, 0) - 1) * 0.08s);
}
.animation-fade-up > *:nth-child(1) { --animation-order: 1; }
.animation-fade-up > *:nth-child(2) { --animation-order: 2; }
.animation-fade-up > *:nth-child(3) { --animation-order: 3; }
.animation-fade-up > *:nth-child(4) { --animation-order: 4; }
.animation-fade-up > *:nth-child(5) { --animation-order: 5; }
.animation-fade-up > *:nth-child(6) { --animation-order: 6; }
.animation-fade-up > *:nth-child(7) { --animation-order: 7; }
.animation-fade-up > *:nth-child(8) { --animation-order: 8; }
.animation-fade-up > *:nth-child(9) { --animation-order: 9; }
.animation-fade-up > *:nth-child(10) { --animation-order: 10; }
.animation-fade-up > *:nth-child(11) { --animation-order: 11; }
.animation-fade-up > *:nth-child(12) { --animation-order: 12; }
.animation-fade-up > *:nth-child(13) { --animation-order: 13; }
.animation-fade-up > *:nth-child(14) { --animation-order: 14; }
.animation-fade-up > *:nth-child(15) { --animation-order: 15; }
.animation-fade-up > *:nth-child(16) { --animation-order: 16; }
.animation-fade-up > *:nth-child(17) { --animation-order: 17; }
.animation-fade-up > *:nth-child(18) { --animation-order: 18; }
.animation-fade-up > *:nth-child(19) { --animation-order: 19; }
.animation-fade-up > *:nth-child(20) { --animation-order: 20; }

View file

@ -1,7 +1,7 @@
/* GitHub Card */
.gc-container {
--at-apply: 'block mb-4 px-5 py-4 overflow-x-auto uno-round-border bg-secondary/5';
--at-apply: 'transition-colors lg:(px-6 py-5) hover:(bg-secondary/10 c-primary)';
--at-apply: 'transition-colors duration-300 ease-out lg:(px-6 py-5) hover:(bg-secondary/10 c-primary)';
scrollbar-color: oklch(var(--un-preset-theme-colors-secondary) / 0.15) transparent;
}

View file

@ -30,7 +30,7 @@ h4:hover .heading-anchor-link svg {
--at-apply: 'op-80';
}
.heading-anchor-link svg {
--at-apply: 'ml-0.4em aspect-square w-0.9em op-0 transition-opacity active:scale-90';
--at-apply: 'ml-0.4em aspect-square w-0.9em op-0 transition-opacity duration-300 ease-out active:scale-90';
}
h1:hover .heading-anchor-link svg:hover,
h2:hover .heading-anchor-link svg:hover,
@ -65,41 +65,6 @@ h4:hover .heading-anchor-link svg:hover {
}
}
/* View Transition with Theme Toggle >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
::view-transition-new(theme-transition) {
animation: reveal 1s cubic-bezier(0.4, 0, 0.2, 1);
clip-path: inset(0 0 0 0);
z-index: 99;
}
::view-transition-old(theme-transition) {
animation: none;
z-index: -1;
}
@keyframes reveal {
from {
clip-path: inset(var(--from));
}
}
html.dark {
--from: 0 0 100% 0;
}
html:not(.dark) {
--from: 100% 0 0 0;
}
/* Disable animations for other elements during theme switching */
html[data-theme-transition] [data-disable-transition-on-theme] {
view-transition-name: none !important;
}
/* Fallback animation when view-transition-name is not supported */
@supports not (view-transition-name: none) {
html:not([data-restore-theme]) {
--at-apply: 'transition-colors duration-300 ease-out';
}
}
/* Snell Roundhand Static Font >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */
@font-face {
font-family: "Snell-Bold";

View file

@ -60,7 +60,7 @@
/* Links */
.heti :where(a:not(.gc-container)) {
--at-apply: 'break-all font-semibold tracking-0 underline underline-0.075em decoration-secondary/80 underline-offset-0.1em';
--at-apply: 'transition-colors hover:(c-primary decoration-primary/80) lg:underline-0.1em';
--at-apply: 'transition-colors duration-300 ease-out hover:(c-primary decoration-primary/80) lg:underline-0.1em';
}
/* Images */

View file

@ -56,7 +56,7 @@ export default defineConfig({
],
shortcuts: {
'uno-desktop-column': 'fixed right-[max(5rem,calc(50vw-35rem))] w-14rem',
'uno-tags-style': 'inline-block whitespace-nowrap border border-secondary/25 rounded-full px-3.2 py-0.7 c-secondary transition-colors hover:(border-secondary/80 text-primary)',
'uno-tags-style': 'inline-block whitespace-nowrap border border-secondary/25 rounded-full px-3.2 py-0.7 c-secondary transition-colors duration-300 ease-out hover:(border-secondary/80 text-primary)',
'uno-decorative-line': 'mb-4.5 h-0.25 w-10 bg-secondary/25 lg:(mb-6 w-11)',
'uno-tags-wrapper': 'flex flex-wrap gap-x-3 gap-y-3.2',
'uno-round-border': 'border border-secondary/5 rounded border-solid',