Files
sui_blog/src/components/GsapReveal.tsx
T
胡旭 18e915bcbb fix: 修复后台文章计数为0 + 分类编辑补全描述字段 + CSP放行外部图片 + 更新关于页描述
- 文章管理:计数请求补 page=1 参数,命中分页接口返回正确的 total
- 分类管理:编辑模式新增描述输入框,保存时一并提交 description
- CSP:img-src 加入 https: 允许加载外部图片
- 关于页:数据存储描述从 JSON 文件更正为 SQLite 数据库
- Footer:添加 ICP 备案号
2026-06-24 13:51:48 +08:00

87 lines
2.2 KiB
TypeScript

"use client";
import { useEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
interface GsapRevealProps {
children: React.ReactNode;
variant?: "fade-up" | "fade-in" | "slide-left" | "slide-right" | "scale";
delay?: number;
duration?: number;
stagger?: number;
className?: string;
once?: boolean;
}
const VARIANTS = {
"fade-up": { y: 40, opacity: 0 },
"fade-in": { opacity: 0 },
"slide-left": { x: -40, opacity: 0 },
"slide-right": { x: 40, opacity: 0 },
scale: { scale: 0.92, opacity: 0 },
} as const;
export default function GsapReveal({
children,
variant = "fade-up",
delay = 0,
duration = 0.8,
stagger = 0,
className = "",
once = true,
}: GsapRevealProps) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
// 关键降级:尊重用户系统设置,无障碍优先,不做任何位移。
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduceMotion) return;
const targets = el.children.length > 1 ? Array.from(el.children) : [el];
const ctx = gsap.context(() => {
if (stagger > 0 && targets.length > 1) {
targets.forEach((child, i) => {
gsap.from(child, {
...VARIANTS[variant],
duration,
delay: delay + i * stagger,
ease: "power3.out",
scrollTrigger: {
trigger: child,
start: "top 92%",
toggleActions: once ? "play none none none" : "play none none reverse",
},
});
});
} else {
gsap.from(targets, {
...VARIANTS[variant],
duration,
delay,
ease: "power3.out",
scrollTrigger: {
trigger: el,
start: "top 88%",
toggleActions: once ? "play none none none" : "play none none reverse",
},
});
}
}, el);
return () => ctx.revert();
}, [variant, delay, duration, stagger, once]);
return (
<div ref={ref} className={className}>
{children}
</div>
);
}