"use client"; import Link from "next/link"; import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import type { PublicPost } from "@/lib/store"; import { formatDate, readingTimeLabel } from "@/lib/utils"; import { useGsapAnimation } from "./useGsapAnimation"; gsap.registerPlugin(ScrollTrigger); export default function PostContent({ post, prevPost, nextPost, }: { post: PublicPost; prevPost: PublicPost | null; nextPost: PublicPost | null; }) { const ref = useGsapAnimation((scope, isReduced) => { if (isReduced) return; const tl = gsap.timeline(); tl.from(".post-back", { x: -20, opacity: 0, duration: 0.5, ease: "power3.out" }); tl.from(".post-category", { y: 15, opacity: 0, duration: 0.5, ease: "power3.out" }, "-=0.2"); // 标题逐字 —— 用 scope 内选择器,而非全局 document const titleChars = scope.querySelectorAll(".post-title-char"); tl.from( titleChars, { y: 30, opacity: 0, filter: "blur(4px)", duration: 0.5, stagger: 0.03, ease: "power3.out" }, "-=0.3" ); tl.from(".post-meta", { y: 10, opacity: 0, duration: 0.4, ease: "power3.out" }, "-=0.2"); tl.from(".post-divider-line", { scaleX: 0, duration: 0.6, ease: "power2.inOut", stagger: 0.1 }, "-=0.2"); tl.from(".post-divider-dot", { scale: 0, duration: 0.3, ease: "back.out(2)" }, "-=0.3"); // 正文段落滚动揭示 const paragraphs = scope.querySelectorAll(".prose-literary > *"); paragraphs.forEach((p) => { gsap.from(p, { y: 25, opacity: 0, duration: 0.6, ease: "power3.out", scrollTrigger: { trigger: p, start: "top 90%" }, }); }); gsap.from(".post-tag", { scale: 0.8, opacity: 0, duration: 0.4, stagger: 0.05, ease: "back.out(1.5)", scrollTrigger: { trigger: ".post-tags", start: "top 90%" }, }); gsap.from(".post-nav", { y: 20, opacity: 0, duration: 0.5, stagger: 0.1, ease: "power3.out", scrollTrigger: { trigger: ".post-navs", start: "top 90%" }, }); }, [post.slug]); // 标题拆字 const titleChars = [...post.title].map((char, i) => ( {char} )); return (
} className="px-page max-w-5xl mx-auto pt-12 pb-24"> {/* Back link */}
返回文章列表
{/* Header */}
{post.category}

{titleChars}

{readingTimeLabel(post.readingTime)}
{/* Cover image */} {post.coverImage && (
{/* eslint-disable-next-line @next/next/no-img-element */} {post.title}
)} {/* Decorative divider */}
{/* Content — 已在 store 写入时净化,渲染时再次净化以防御历史脏数据 */}
{/* Tags — 点击跳转到 /blog 按标签筛选 */}
{post.tags.map((tag) => ( #{tag} ))}
{/* Prev / Next navigation */}
{prevPost ? ( 上一篇 {prevPost.title} ) : (
)} {nextPost ? ( 下一篇 {nextPost.title} ) : (
)}
); }