import { cn, getStaticImage } from '@/lib/utils' import { type ReactNode, useEffect, useMemo, useState } from 'react' import { FlipActionButton, Image } from '@/components' import { motion } from 'framer-motion' const OVERLAY_STYLE: React.CSSProperties = { position: 'fixed', inset: 0, zIndex: 9999, pointerEvents: 'none', } const isDirectImageSrc = (src: string) => src.startsWith('http://') || src.startsWith('https://') || src.startsWith('/') || src.startsWith('data:image/') const resolveImageSrc = (src: string) => (isDirectImageSrc(src) ? src : getStaticImage(src)) export interface ExhibitFlipOverlayProps { /** 展品正面图片地址,可以是静态资源路径或完整 URL */ src: string /** 展品翻面(背面)图片地址,可以是静态资源路径或完整 URL */ flipSrc?: string /** * 展品默认展示缩放(用于“默认放大”) * - 1 表示原尺寸逻辑 * - > 1 会放大展品展示(仍受 max-w/max-h 约束) */ displayScale?: number /** 展品入场动画播放完成(背景/按钮出现前后) */ onEnterComplete?: () => void /** 关闭回调 */ onClose?: () => void /** 是否可见,默认 true */ visible?: boolean /** 额外的自定义类 */ className?: string /** 底部操作区 */ footer?: ReactNode /** 底部操作区额外类名 */ footerClassName?: string } /** * 轻量展品翻转层 * - 不使用 Portal * - 不支持缩放/拖拽 * - 点击展品区域翻转显示另一张图(带 3D 翻转动画) */ const ExhibitFlipOverlay = ({ src, flipSrc, displayScale = 1, onEnterComplete, onClose, visible = true, className, footer, footerClassName, }: ExhibitFlipOverlayProps) => { const backSrc = getStaticImage('components/close') const [mounted, setMounted] = useState(false) const [flipped, setFlipped] = useState(false) const [showChrome, setShowChrome] = useState(false) useEffect(() => { setMounted(true) }, []) useEffect(() => { if (visible) { setFlipped(false) setShowChrome(false) } }, [visible, src, flipSrc]) const frontImageSrc = resolveImageSrc(src) const backImageSrc = useMemo(() => { if (!flipSrc) return null return resolveImageSrc(flipSrc) }, [flipSrc]) const canFlip = Boolean(backImageSrc && backImageSrc !== frontImageSrc) if (!mounted || !visible) return null return (
{/* 第二段:背景与按钮在展品入场后再出现 */} {showChrome && ( <> {onClose && ( { e.stopPropagation() onClose() }} alt="返回" button /> )} )}
{/* 第一段:像“获得装备”一样先把展品弹出 */} { if (!showChrome) { setShowChrome(true) onEnterComplete?.() } }} style={{ transformStyle: 'preserve-3d', WebkitTransformStyle: 'preserve-3d', }} >
展品
{canFlip && (
展品背面
)}
{showChrome && canFlip && ( { e.stopPropagation() setFlipped((v) => !v) }} > 翻转一下 )}
{showChrome && footer && ( {footer} )}
) } export default ExhibitFlipOverlay