167 lines
6.7 KiB
TypeScript
167 lines
6.7 KiB
TypeScript
import { useState } from "react";
|
|
import { AnimatePresence, motion } from "framer-motion";
|
|
import { getStaticImage } from "@/lib/utils";
|
|
import { Image } from "@/components";
|
|
|
|
export interface LetterToLuxunProps {
|
|
/** 是否可见 */
|
|
visible?: boolean;
|
|
/** 关闭回调 */
|
|
onClose?: () => void;
|
|
/** 提交回调 */
|
|
onSubmit?: (values: string[]) => void;
|
|
}
|
|
|
|
/**
|
|
* LetterToLuxun 组件 - 写给鲁迅先生的信
|
|
* 包含飞入和放大动画
|
|
*/
|
|
const LetterToLuxun = ({
|
|
visible = false,
|
|
onClose,
|
|
onSubmit,
|
|
}: LetterToLuxunProps) => {
|
|
// 下划线可填写内容
|
|
const [blankValues, setBlankValues] = useState<string[]>([]);
|
|
|
|
// 信件内容模板(根据设计稿)
|
|
const letterContent = `敬爱的鲁迅先生:
|
|
|
|
您好!
|
|
|
|
今天,我怀着崇敬的心情走进大陆新村9号,看着您亲手撰写的《呐喊》《且介亭杂文》《故事新编》等作品,您编辑的《萌芽》《前哨》等杂志,您翻译的《艺术论》《死魂灵》等译文,仿佛能看到您"舌将上下而求索"的身影。在这场跨越时空的对话中,我终于明白您坚守的"____",不是抽象的理念,而是我们安身立命的根本;您秉持的"____",不是无谓的锋芒,而是我们直面现实的勇气。
|
|
|
|
我是____专业的学生,今后,我会以您"____干夫指"的骨气,在是非面前坚守立场,不随波逐流;我会以您"俯首甘为____"的热忱,参与____活动,用微光温暖他人;我更会记住您"____,走的人多了,便成了路"的箴言,在专业领域勇于探索,努力用____为民族复兴、文化繁荣添砖加瓦。
|
|
|
|
请您放心,您播下的"立人"火种,我们会让它在新时代燎原;您毕生追求的"人国"愿景,我们会用青春和奋斗去守护!`;
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{visible && (
|
|
<>
|
|
{/* 遮罩层 */}
|
|
<motion.div
|
|
className="fixed inset-0 bg-black/50 z-[100]"
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
onClick={onClose}
|
|
/>
|
|
|
|
{/* 信件容器 */}
|
|
<motion.div
|
|
className="fixed inset-0 z-[101] flex items-center justify-center pointer-events-none"
|
|
initial={{ opacity: 0, scale: 0.5, y: 100 }}
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
exit={{ opacity: 0, scale: 0.5, y: 100 }}
|
|
transition={{
|
|
type: "spring",
|
|
stiffness: 300,
|
|
damping: 30,
|
|
duration: 0.6,
|
|
}}
|
|
>
|
|
<div
|
|
className="relative w-[1200px] h-full pointer-events-auto"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{/* 信纸背景 */}
|
|
<img
|
|
src={getStaticImage("components/letter/信底")}
|
|
alt="信纸"
|
|
className="absolute inset-0 w-full h-full object-contain"
|
|
/>
|
|
|
|
{/* 标题 - 垂直显示在左侧 */}
|
|
<div className="absolute left-[160px] top-20 bottom-0 [text-shadow:_0px_0px_4px_#b37921,_6px_6px_4px_#b37921,_6px_6px_4px_#b37921,_6px_6px_4px_#b37921]">
|
|
<div
|
|
className="text-white text-6xl font-bold tracking-[20px]"
|
|
style={{
|
|
writingMode: "vertical-rl",
|
|
textOrientation: "upright",
|
|
}}
|
|
>
|
|
写给鲁迅先生的信
|
|
</div>
|
|
</div>
|
|
|
|
{/* 信件内容区域 */}
|
|
<div className="absolute w-[700px] left-[320px] right-[180px] top-[80px] bottom-[180px] z-99">
|
|
<div className="text-[#4e402e] text-[26px] leading-relaxed">
|
|
{(() => {
|
|
let placeholderIndex = 0;
|
|
return letterContent.split("\n").map((line, lineIndex) => {
|
|
const segments = line.split("____");
|
|
return (
|
|
<div key={lineIndex} className="mb-2">
|
|
{segments.map((part, partIndex) => {
|
|
const isLast = partIndex === segments.length - 1;
|
|
if (isLast) {
|
|
return (
|
|
<span key={`part-${lineIndex}-${partIndex}`}>
|
|
{part}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
const currentIndex = placeholderIndex++;
|
|
return (
|
|
<span key={`part-${lineIndex}-${partIndex}`}>
|
|
{part}
|
|
<input
|
|
className="inline-block w-[100px] h-[36px] mx-2 align-middle bg-transparent border-b-2 border-dashed border-[#4e402e] text-[24px] text-center outline-none"
|
|
value={blankValues[currentIndex] ?? ""}
|
|
onChange={(e) => {
|
|
const next = [...blankValues];
|
|
next[currentIndex] = e.target.value;
|
|
setBlankValues(next);
|
|
}}
|
|
/>
|
|
</span>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
});
|
|
})()}
|
|
</div>
|
|
</div>
|
|
|
|
{/* 笔的图片 */}
|
|
<div className="absolute bottom-[150px] -right-30">
|
|
<img
|
|
src={getStaticImage("components/letter/笔")}
|
|
alt="笔"
|
|
className="w-full h-full object-contain"
|
|
/>
|
|
</div>
|
|
|
|
{/* 提交按钮 */}
|
|
<div className="absolute bottom-[80px] left-1/2 transform -translate-x-1/2 z-99">
|
|
<Image
|
|
src={getStaticImage("components/letter/可提交状态")}
|
|
alt="可提交状态"
|
|
onClick={() => onSubmit?.(blankValues)}
|
|
button
|
|
/>
|
|
</div>
|
|
|
|
{/* 寄信图标(可选,如果需要的话) */}
|
|
<div className="absolute top-[50px] right-[50px] w-[100px] h-[100px] opacity-80">
|
|
<img
|
|
src={getStaticImage("components/letter/寄信图标")}
|
|
alt="寄信"
|
|
className="w-full h-full object-contain"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
};
|
|
|
|
export default LetterToLuxun;
|