feat:添加黄炎培成长之路页面内容;学业选择框和思想选择框
This commit is contained in:
1 parent
ae0c050010
commit
66f460633a
5 files changed
+455
-29
No files matched your search
@@ -0,0 +1,191 @@
|
||||
import { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { cn, getStaticImage } from "@/lib/utils";
|
||||
import { Image } from "@/components";
|
||||
import { useScale } from "@/contexts/ScaleContext";
|
||||
|
||||
export interface ChoiceDialog2Props {
|
||||
/** 是否可见 */
|
||||
visible?: boolean;
|
||||
/** 关闭回调 */
|
||||
onClose?: () => void;
|
||||
/** 选择回调 */
|
||||
onSelect?: (choice: "铁工科" | "商科") => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二个选择弹窗:背景图选择
|
||||
* - 使用蒙版遮罩作为背景
|
||||
* - 显示两个选择:10_008(箭头指向右边)和旋转后的背景图
|
||||
*/
|
||||
const ChoiceDialog2 = ({
|
||||
visible = true,
|
||||
onClose,
|
||||
onSelect,
|
||||
}: ChoiceDialog2Props) => {
|
||||
const maskSrc = getStaticImage("components/蒙版遮罩");
|
||||
const backSrc = getStaticImage("components/关闭");
|
||||
const choiceSrc = getStaticImage("huangyanpei/reflection/scene2/10_008");
|
||||
|
||||
const { scale, offsetX, offsetY, DESIGN_WIDTH, DESIGN_HEIGHT } = useScale();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// SSR 兼容
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const handleClose = useCallback(
|
||||
(e: React.MouseEvent<HTMLImageElement>) => {
|
||||
e.stopPropagation();
|
||||
onClose?.();
|
||||
},
|
||||
[onClose]
|
||||
);
|
||||
|
||||
const handleChoiceClick = useCallback(
|
||||
(choice: "铁工科" | "商科") => {
|
||||
onSelect?.(choice);
|
||||
onClose?.();
|
||||
},
|
||||
[onSelect, onClose]
|
||||
);
|
||||
|
||||
// 样式
|
||||
const overlayStyle = useMemo<React.CSSProperties>(
|
||||
() => ({
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
zIndex: 9999,
|
||||
pointerEvents: "none",
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const scaledContainerStyle = useMemo<React.CSSProperties>(
|
||||
() => ({
|
||||
position: "absolute",
|
||||
width: `${DESIGN_WIDTH}px`,
|
||||
height: `${DESIGN_HEIGHT}px`,
|
||||
left: `${offsetX}px`,
|
||||
top: `${offsetY}px`,
|
||||
transform: `scale(${scale})`,
|
||||
transformOrigin: "top left",
|
||||
overflow: "hidden",
|
||||
}),
|
||||
[scale, offsetX, offsetY, DESIGN_WIDTH, DESIGN_HEIGHT]
|
||||
);
|
||||
|
||||
if (!mounted) return null;
|
||||
|
||||
const content = (
|
||||
<AnimatePresence>
|
||||
{visible && (
|
||||
<motion.div
|
||||
key="choice-dialog-2"
|
||||
style={overlayStyle}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.3, ease: "easeInOut" }}
|
||||
>
|
||||
<div style={scaledContainerStyle}>
|
||||
<div
|
||||
className={cn(
|
||||
"absolute inset-0 flex flex-col items-center justify-center",
|
||||
"bg-black/70"
|
||||
)}
|
||||
style={{ pointerEvents: "auto" }}
|
||||
>
|
||||
{/* 背景蒙版 */}
|
||||
{maskSrc && (
|
||||
<Image
|
||||
src={maskSrc}
|
||||
className="absolute inset-0 w-full h-full object-cover select-none pointer-events-none"
|
||||
aria-hidden
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 返回按钮 */}
|
||||
{onClose && (
|
||||
<Image
|
||||
src={backSrc}
|
||||
onClick={handleClose}
|
||||
alt="返回"
|
||||
className="absolute top-5 right-5 z-20"
|
||||
button
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 内容区域 */}
|
||||
<div className="relative z-10 w-full h-full flex items-center justify-center">
|
||||
<div className="flex flex-col items-center gap-12">
|
||||
{/* 选择A:铁工科(箭头指向左边) */}
|
||||
<div
|
||||
className="flex items-center gap-6 cursor-pointer transition-transform duration-200 hover:scale-105 active:scale-95"
|
||||
onClick={() => handleChoiceClick("铁工科")}
|
||||
>
|
||||
{/* 标签A */}
|
||||
<div className="text-7xl font-bold text-[#ffa500] [text-shadow:_3px_3px_0_#fff,_-3px_-3px_0_#fff,_3px_-3px_0_#fff,_-3px_3px_0_#fff]">
|
||||
A
|
||||
</div>
|
||||
{/* 箭头按钮 - 需要旋转180度使其指向左边 */}
|
||||
<div
|
||||
className="relative"
|
||||
style={{ transform: "rotate(180deg)" }}
|
||||
>
|
||||
<img
|
||||
src={choiceSrc}
|
||||
alt="铁工科"
|
||||
className="max-w-[800px] object-contain"
|
||||
/>
|
||||
{/* 文字覆盖层 */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div
|
||||
className="text-white text-7xl font-bold"
|
||||
style={{ transform: "rotate(180deg)" }}
|
||||
>
|
||||
铁工科
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 选择B:商科(箭头指向右边) */}
|
||||
<div
|
||||
className="flex items-center gap-6 cursor-pointer transition-transform duration-200 hover:scale-105 active:scale-95"
|
||||
onClick={() => handleChoiceClick("商科")}
|
||||
>
|
||||
{/* 标签B */}
|
||||
<div className="text-7xl font-bold text-[#ffa500] [text-shadow:_3px_3px_0_#fff,_-3px_-3px_0_#fff,_3px_-3px_0_#fff,_-3px_3px_0_#fff]">
|
||||
B
|
||||
</div>
|
||||
{/* 箭头按钮 */}
|
||||
<div className="relative">
|
||||
<img
|
||||
src={choiceSrc}
|
||||
alt="商科"
|
||||
className="max-w-[800px] object-contain"
|
||||
/>
|
||||
{/* 文字覆盖层 */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-white text-7xl font-bold">
|
||||
商科
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
|
||||
return createPortal(content, document.body);
|
||||
};
|
||||
|
||||
export default ChoiceDialog2;
|
||||
Reference in new issue
Block a user