343 lines
13 KiB
TypeScript
343 lines
13 KiB
TypeScript
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
|