Initial 智念AIGC platform
This commit is contained in:
99
lib/ui/motion.ts
Normal file
99
lib/ui/motion.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
"use client";
|
||||
|
||||
import { gsap } from "gsap";
|
||||
|
||||
type ScopeRef = { current: Element | null };
|
||||
|
||||
const ENTER_DURATION = 0.28;
|
||||
const EXIT_DURATION = 0.18;
|
||||
const ENTER_EASE = "power2.out";
|
||||
const EXIT_EASE = "power1.in";
|
||||
|
||||
export function prefersReducedMotion() {
|
||||
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
}
|
||||
|
||||
export function runScopedMotion(scopeRef: ScopeRef, setup: (scope: Element) => void) {
|
||||
const scope = scopeRef.current;
|
||||
if (!scope || prefersReducedMotion()) return undefined;
|
||||
const context = gsap.context(() => setup(scope), scope);
|
||||
return () => context.revert();
|
||||
}
|
||||
|
||||
export function revealChildren(scope: Element, selector = "[data-animate]") {
|
||||
if (prefersReducedMotion()) return;
|
||||
const items = gsap.utils.toArray<HTMLElement>(selector, scope);
|
||||
if (!items.length) return;
|
||||
gsap.fromTo(
|
||||
items,
|
||||
{ autoAlpha: 0, y: 14 },
|
||||
{
|
||||
autoAlpha: 1,
|
||||
y: 0,
|
||||
duration: ENTER_DURATION,
|
||||
ease: ENTER_EASE,
|
||||
stagger: 0.045,
|
||||
clearProps: "transform,opacity,visibility"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function crossfadeIn(element: Element | null, y = 10) {
|
||||
if (!element || prefersReducedMotion()) return;
|
||||
gsap.fromTo(
|
||||
element,
|
||||
{ autoAlpha: 0, y },
|
||||
{
|
||||
autoAlpha: 1,
|
||||
y: 0,
|
||||
duration: ENTER_DURATION,
|
||||
ease: ENTER_EASE,
|
||||
clearProps: "transform,opacity,visibility"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function pulseFeedback(element: Element | null) {
|
||||
if (!element || prefersReducedMotion()) return;
|
||||
gsap.fromTo(
|
||||
element,
|
||||
{ scale: 0.985 },
|
||||
{
|
||||
scale: 1,
|
||||
duration: 0.22,
|
||||
ease: ENTER_EASE,
|
||||
clearProps: "transform"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function modalEnter(element: Element | null) {
|
||||
if (!element || prefersReducedMotion()) return;
|
||||
gsap.fromTo(
|
||||
element,
|
||||
{ autoAlpha: 0, scale: 0.98, y: 10 },
|
||||
{
|
||||
autoAlpha: 1,
|
||||
scale: 1,
|
||||
y: 0,
|
||||
duration: ENTER_DURATION,
|
||||
ease: ENTER_EASE,
|
||||
clearProps: "transform,opacity,visibility"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function modalExit(element: Element | null, onComplete: () => void) {
|
||||
if (!element || prefersReducedMotion()) {
|
||||
onComplete();
|
||||
return;
|
||||
}
|
||||
gsap.to(element, {
|
||||
autoAlpha: 0,
|
||||
scale: 0.985,
|
||||
y: 6,
|
||||
duration: EXIT_DURATION,
|
||||
ease: EXIT_EASE,
|
||||
onComplete
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user