Files
shanghai-residences/src/pages/luxun/reflection/scene1.tsx
T
2026-04-30 09:17:31 +08:00

343 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { Image, StudyGuideModal } from '@/components'
import SuccessAlert from '@/components/layout/alert/success-alert'
import { getBackgroundImage, getPublicPath, getStaticImage } from '@/lib/utils'
import { usePuzzleAnimation } from '@/contexts/PuzzleAnimationContext'
import { useLuxunStore, saveLuxunStore } from '@/store/luxunStore'
const starName = 'luxun'
// 城市配置 - 根据效果图调整位置
// 布局:北京(上) -> 上海(中上) -> 绍兴(中) -> 广州(左下) -> 厦门(右下)
const cityConfig = [
{
id: 1,
name: '绍兴',
location: '绍兴三味书屋',
imagePath: 'luxun/reflection/scene1/绍兴',
position: { top: '65%', left: '65%' }, // 中间
iconPosition: { top: '0%', left: '0%' },
time: '1892年-1898年',
description: '少年鲁迅在此求学,为《从百草园到三味书屋》埋下创作种子。',
},
{
id: 2,
name: '北京',
location: '北京八道湾胡同',
imagePath: 'luxun/reflection/scene1/北京',
position: { top: '14%', left: '62%' }, // 顶部
iconPosition: { top: '43%', left: '0%' },
time: '1919-1923年',
description: '鲁迅参与新文化运动的核心时期,《阿Q正传》等经典作品在此酝酿创作。',
},
{
id: 3,
name: '厦门',
location: '厦门大学',
imagePath: 'luxun/reflection/scene1/厦门',
position: { top: '83%', left: '78%' }, // 右下
iconPosition: { top: '16%', left: '40%' },
time: '1926年9-12月',
description: '鲁迅应邀前往厦门大学任教,期间写下多篇批判现实的杂文,关注底层民众命运。',
},
{
id: 4,
name: '广州',
location: '广州白云楼',
imagePath: 'luxun/reflection/scene1/广州',
position: { top: '88%', left: '35%' }, // 左下
iconPosition: { top: '16%', left: '30%' },
time: '1927年3-9月',
description: '先生在此整理旧作,思考民族觉醒之路,《朝花夕拾》部分篇章定稿于此。',
},
{
id: 5,
name: '上海',
location: '上海鲁迅故居',
imagePath: 'luxun/reflection/scene1/上海',
position: { top: '48%', left: '70%' }, // 中上
iconPosition: { top: '0%', left: '0%' },
time: '1933-1936年',
description:
'鲁迅晚年定居地,先生在此写下《故事新编》等作品,与进步文人并肩,以文字为刃批判黑暗。',
},
]
const LuxunReflectionScene1 = () => {
const navigate = useNavigate()
const { moduleName } = useParams<{
moduleName: string
}>()
//碎片动画触发方法
const { triggerPuzzleAnimation } = usePuzzleAnimation()
const { setReflectionRoute } = useLuxunStore()
// 已点击的城市ID集合
const [clickedCities, setClickedCities] = useState<Set<number>>(new Set())
// 是否显示成功提示(全部完成时)
const [showSuccessAlert, setShowSuccessAlert] = useState(false)
// 当前显示的城市提示ID(单个地点点击后)
const [currentCityAlertId, setCurrentCityAlertId] = useState<number | null>(null)
// 是否全部完成
const [allCompleted, setAllCompleted] = useState(false)
// 学习引导对话是否显示
const [showGuideModal, setShowGuideModal] = useState(true)
const progress = clickedCities.size
// 当前应当点亮的城市索引(按照 cityConfig 顺序)
const nextCityIndex = progress
const nextCityId = cityConfig[nextCityIndex]?.id
const totalTasks = cityConfig.length
// 检查是否全部完成
useEffect(() => {
if (progress === totalTasks && !allCompleted) {
setAllCompleted(true)
}
}, [progress, totalTasks, allCompleted])
const handleCityClick = (cityId: number) => {
// 仅允许按照 cityConfig 顺序点击
if (clickedCities.has(cityId)) return // 已点击过的不再处理
if (cityId !== nextCityId) return // 不是当前应点击的城市则不响应
setClickedCities((prev) => {
const next = new Set(prev)
next.add(cityId)
return next
})
// 显示当前城市的成功提示
setCurrentCityAlertId(cityId)
}
const handleSuccessAlertClose = () => {
setShowSuccessAlert(false)
}
const handleCityAlertClose = () => {
const cityId = currentCityAlertId
setCurrentCityAlertId(null)
// 如果关闭的是最后一个城市(上海鲁迅故居,id=5),则显示全部完成的提示
const lastCity = cityConfig[cityConfig.length - 1]
if (lastCity && cityId === lastCity.id) {
triggerPuzzleAnimation(14)
setShowSuccessAlert(true)
}
}
// 获取当前显示的城市信息
const currentCity = currentCityAlertId
? cityConfig.find((city) => city.id === currentCityAlertId)
: null
return (
<div
className="flex justify-between w-full h-full bg-cover bg-center bg-no-repeat gap-8 relative"
style={{
backgroundImage: getBackgroundImage(`${starName}/reflection/scene1/底图`),
}}
>
{/* 离开按钮 */}
<Image
className="absolute right-5 top-5 z-10"
src={getStaticImage(`${starName}/reflection/scene1/离开`)}
button
onClick={() => {
if (moduleName && starName) {
navigate(`/${moduleName}/${starName}/home`)
}
}}
/>
{/* 左侧:鲁迅的文学足迹 - 地图区域 */}
<div className="w-[45%] relative">
{/* 地图容器 */}
<div className="relative w-full h-full">
{cityConfig.map((city) => {
const isClicked = clickedCities.has(city.id)
const isNext = city.id === nextCityId
return (
<div
key={city.id}
className="absolute flex flex-col items-center cursor-pointer hover:scale-110 transition-all duration-300"
style={{
top: city.position.top,
left: city.position.left,
transform: 'translate(-50%, -50%)',
}}
onClick={() => handleCityClick(city.id)}
>
{/* 城市图片容器 */}
<div className="relative">
<img
src={getStaticImage(city.imagePath)}
alt={city.name}
className={`transition-all duration-300 ${
isClicked ? 'brightness-100' : 'brightness-50 grayscale'
}`}
style={{
filter: isClicked ? 'none' : 'grayscale(100%) brightness(0.5)',
}}
/>
{/* 点击图标和提示 - 覆盖在城市图片上 */}
<div
className="absolute -translate-x-full -translate-y-full flex flex-col items-center z-10 pointer-events-none"
style={{
left: city.iconPosition.left,
top: city.iconPosition.top,
}}
>
{/* 点一点提示 - 始终显示 */}
{/* 仅对当前应点击的城市显示点一点提示 */}
{!isClicked && isNext && (
<div className="flex items-center">
<img
src={getStaticImage(`${starName}/reflection/scene1/鲁迅体悟改版点一点`)}
alt="点一点"
/>
<img
className="animate-finger-bounce scale-y-[-1]"
src={getStaticImage(`${starName}/reflection/scene1/鲁迅体悟点一点图标`)}
alt="点一点图标"
/>
</div>
)}
</div>
</div>
</div>
)
})}
</div>
</div>
{/* 导航栏 */}
<div className="absolute top-5 left-[37%] w-80 space-y-4 z-99">
{/* 任务1点亮图标 */}
<div className="text-4xl font-bold h-16 cursor-pointer"></div>
{/* 任务2漫集美学 */}
<div
className="text-4xl font-bold h-16 cursor-pointer relative"
onClick={() => {
if (moduleName && allCompleted) {
setReflectionRoute('reflection2')
saveLuxunStore()
navigate(`/${moduleName}/${starName}/reflection2`)
}
}}
>
{/* 任务完成后显示点一点图标 */}
{allCompleted && (
<div className="absolute -right-20 top-1/2 flex items-center gap-2 z-10 pointer-events-none">
<img
className="animate-finger-bounce scale-x-[-1]"
src={getStaticImage(`${starName}/reflection/scene1/鲁迅体悟点一点图标`)}
alt="点一点图标"
/>
</div>
)}
</div>
{/* 任务3学习精神 */}
<div className="text-4xl font-bold h-16 cursor-pointer"></div>
</div>
{/* 右侧区域 */}
<div className="flex-1 relative">
{/* 任务进度 */}
<div className="absolute top-5 left-[24%] w-[445px] h-55 flex justify-center items-center">
<span className="text-white text-[54px] font-bold [text-shadow:_0px_0px_4px_#b37921,_6px_6px_4px_#b37921,_6px_6px_4px_#b37921,_6px_6px_4px_#b37921]">
{allCompleted ? '任务进度已完成' : `任务进度:(${progress}/${totalTasks})`}
</span>
</div>
{/* 任务展示窗口 */}
<div className="absolute bottom-0 right-7 w-[995px] h-[728px] font-bold text-6xl px-10 py-10 ">
{allCompleted ? (
// 全部完成后显示详细信息
<div className="space-y-10 h-full overflow-y-auto custom-scrollbar">
{cityConfig.map((city) => (
<div key={city.id} className="space-y-2">
<div className="text-red-600 text-3xl flex items-end gap-6">
<div>{city.location}</div>
<div className="text-gray-600 text-2xl font-normal">{city.time}</div>
</div>
<div className="text-black text-2xl font-normal leading-relaxed">
{city.description}
</div>
</div>
))}
</div>
) : (
<div className="space-y-16">
{
// 未完成时显示任务列表
cityConfig.map((city, index) => {
const isCompleted = clickedCities.has(city.id)
return (
<div key={city.id} className={isCompleted ? 'text-green-600' : ''}>
{index + 1}.{city.location}
</div>
)
})
}
</div>
)}
</div>
</div>
{/* 单个地点点击成功提示弹窗 */}
{currentCity && (
<SuccessAlert
visible={true}
title=""
// title={`你找到了${currentCity.location}!`}
content={`${currentCity.time},\n${currentCity.description}`}
onClose={handleCityAlertClose}
onContinue={handleCityAlertClose}
/>
)}
{/* 全部完成成功提示弹窗 */}
<SuccessAlert
visible={showSuccessAlert}
title="你找到了鲁迅创作的足迹,接下来,填充文字里的故事吧!"
onClose={handleSuccessAlertClose}
onContinue={handleSuccessAlertClose}
/>
{/* 学习引导对话 */}
{/* <GuideSteps
visible={guideVisible}
onVisibleChange={setGuideVisible}
showMask={false}
highlightSelectors={[]}
zIndex={10000}
>
<GuideStepsItem speaker="guide">
跟着先生的脚步, 解锁文字里的温度与力量!
</GuideStepsItem>
<GuideStepsItem speaker="guide">
请根据年代由远及近的顺序,依“手指提示”点亮先生的创作足迹,解锁更多的故事背景!
</GuideStepsItem>
</GuideSteps> */}
<StudyGuideModal
visible={showGuideModal}
onContinue={() => {
setShowGuideModal(false)
}}
audioSrc={getPublicPath('audio/luxun/mp3_luxun_31.mp3')}
contentPaddingLeft="2em"
// firstLineIndent="2em"
>
{`同学你好,欢迎来到“跨越时空·体悟”学习模块!
请登陆“时空回溯・亲历时代”体验仓,下一站:1892年,绍兴三味书屋。
请按照年代由远及近的顺序,依“手指提示”,点亮先生的创作足迹,解锁更多故事背景!时空门已开,请做好准备吧!`}
</StudyGuideModal>
</div>
)
}
export default LuxunReflectionScene1