feat(FontApplicator): implement an appearance animation based on existed intersection observer logic and add a reduced motion check
All checks were successful
Workflow / build (pull_request) Successful in 50s
All checks were successful
Workflow / build (pull_request) Successful in 50s
This commit is contained in:
@@ -3,9 +3,10 @@
|
||||
Loads fonts from fontshare with link tag
|
||||
- Loads font only if it's not already applied
|
||||
- Uses IntersectionObserver to detect when font is visible
|
||||
- Adds smooth transition when font is loaded
|
||||
- Adds smooth transition when font appears
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { motion } from '$shared/lib';
|
||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||
import type { Snippet } from 'svelte';
|
||||
import { appliedFontsManager } from '../../model';
|
||||
@@ -32,25 +33,43 @@ interface Props {
|
||||
let { name, id, className, children }: Props = $props();
|
||||
let element: Element;
|
||||
|
||||
// Track if the user has actually scrolled this into view
|
||||
let hasEnteredViewport = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
if (entries[0].isIntersecting) {
|
||||
hasEnteredViewport = true;
|
||||
appliedFontsManager.touch([id]);
|
||||
|
||||
// Once it has entered, we can stop observing to save CPU
|
||||
observer.unobserve(element);
|
||||
}
|
||||
});
|
||||
observer.observe(element);
|
||||
return () => observer.disconnect();
|
||||
});
|
||||
|
||||
const isLoading = $derived(appliedFontsManager.getFontStatus(id) === 'loading');
|
||||
const status = $derived(appliedFontsManager.getFontStatus(id));
|
||||
// The "Show" condition: Element is in view AND (Font is ready OR it errored out)
|
||||
const shouldReveal = $derived(hasEnteredViewport && (status === 'loaded' || status === 'error'));
|
||||
|
||||
const transitionClasses = $derived(
|
||||
motion.reduced
|
||||
? 'transition-none' // Disable CSS transitions if motion is reduced
|
||||
: 'transition-all duration-700 ease-[cubic-bezier(0.22,1,0.36,1)]',
|
||||
);
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={element}
|
||||
style:font-family={name}
|
||||
class={cn(
|
||||
'transition-all duration-200 ease-out',
|
||||
isLoading ? 'opacity-0 translate-y-1' : 'opacity-100 translate-y-0',
|
||||
transitionClasses,
|
||||
// If reduced motion is on, we skip the transform/blur entirely
|
||||
!shouldReveal && !motion.reduced && 'opacity-0 translate-y-8 scale-[0.98] blur-sm',
|
||||
!shouldReveal && motion.reduced && 'opacity-0', // Still hide until font is ready, but no movement
|
||||
shouldReveal && 'opacity-100 translate-y-0 scale-100 blur-0',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user