endpild-laman/pages/index.tsx
AmaneSerenetia 1104b76e46 first commit
2024-01-22 20:52:38 +09:00

290 lines
11 KiB
TypeScript

import type { NextPage } from 'next';
import { useRouter } from 'next/router';
import { useAtom } from "jotai";
import { useEffect, useState } from 'react';
import { AnimatePresence } from "framer-motion";
import { Box } from "@chakra-ui/react";
import { AvailableLanguages, ImageData, IsPortrait, Language, LanguagePack } from "@states/global";
import { AssetLoader } from "@utils/loader";
import { findNearestMultiple, LanguagePack as _LanguagePack, localGet, localSet, Nullable, useLocale, waitAsync } from "@utils/common";
import { Footer } from "@components/footer";
import { Header } from '@components/header';
import { IntroLogo } from "@components/logo/IntroLogo";
import { Body } from '@components/body';
import { LogoLarge_EN } from "@components/logo/EN/EN-big";
import { LogoLarge_CN } from "@components/logo/CN/CN-big.v2";
import { LogoLarge_JP } from "@components/logo/JP/JP-big";
import { LogoLarge_KR } from "@components/logo/KR/KR-big";
import { MotionBox, MotionFlex } from '@components/motion';
interface PageProps {
lang: string,
langPack: string,
fullIntro: Nullable<string>,
}
function getIntroAnimSpeed(lang: string)
{
switch (lang) {
default:
case 'en':
return 1.35;
case 'cn':
return 1.545;
case 'jp':
return 2.3;
case 'kr':
return 1.8;
}
}
function getPostIntroAnimSpeed(lang: string)
{
switch (lang) {
default:
return null;
case 'kr':
return 1.5;
}
}
const Home: NextPage<PageProps> = ({ lang, fullIntro, langPack }) => {
const [progressPercentage , setProgressPercentage] = useState(0);
const [finishedLoading, setFinishedLoading] = useState(false);
const [logoVisible, setLogoVisible] = useState(false);
const [introVisible, setIntroVisible] = useState(false);
const [introFinished, setIntroFinished] = useState(false);
const [dontAnimate, setDontAnimate] = useState<Nullable<boolean>>(true);
const initalLangPack: Partial<_LanguagePack> = { [lang]: JSON.parse(langPack) };
const [currentLang, setCurrentLang] = useAtom(Language);
const [, setLangPack] = useAtom(LanguagePack);
const [, setImageData] = useAtom(ImageData);
const locale = useLocale(useAtom(Language)[0], initalLangPack);
const loadDependencies = [
...[
...new Array(8)
.fill(0)
.map((_, i) => `0${i + 1}_HD`),
...[
"bg",
"world_bg",
"department_bg",
]
].map((i) => ({
url: `assets/img/${i}.jpg`,
overrideOptions: { mimeType: 'image/jpeg' }
})),
...AvailableLanguages
.filter(l => l !== lang)
.map(l => ({ url: `assets/lang/${l}.json`, overrideOptions: { responseType: 'json', mimeType: 'application/json' } })),
];
const [progressData, setProgressData] = useState({
total: loadDependencies.length,
loaded: 0,
success: 0,
_lastPercentage: 0,
});
const router = useRouter();
if (!fullIntro && 'fullIntro' in router.query)
fullIntro = router.query.fullIntro?.toString() ?? null;
useEffect(() => {
setLangPack(initalLangPack);
if (currentLang !== lang) setCurrentLang(lang);
if (localGet('fullIntro') === 'true' && fullIntro === null)
void router.push({
pathname: '/',
query: {
fullIntro: 'true',
}
});
console.log(lang);
let animateLogo = true;
if (fullIntro === 'true' || (fullIntro === null && localGet('fullIntro') === 'true'))
document.body.style.setProperty('--anim-playback-rate', getIntroAnimSpeed(lang ?? '').toString());
else
{
let time = localGet('endfield-time');
if (!time)
localSet('endfield-time', new Date().toISOString() + "@first");
else
{
if (time.includes('@first'))
localSet('endfield-time', time.replace('@first', ''));
else
{
const timeThen = new Date().getTime();
const timeDiff = Date.now() - timeThen;
if (timeDiff < 1000 * 60 * 60 * 24)
animateLogo = false;
else
localSet('endfield-time', new Date(timeThen + 1000 * 60 * 60 * 24).toISOString());
}
}
}
setDontAnimate(animateLogo ? null : true);
(async () => {
const loader = new AssetLoader(
loadDependencies,
{
responseType: 'arraybuffer',
onProgressUpdate: (v, requests) => {
const percentage = Math.floor(v * 100);
setProgressData(prev => {
if (percentage >= findNearestMultiple(100 / requests.length, prev._lastPercentage)) {
let success = 0, loaded = 0;
for (const request of requests)
{
if (request.success)
{
success++;
continue;
}
if (request.progress === 1)
loaded++;
}
return {
total: requests.length,
loaded: loaded + success,
success,
_lastPercentage: percentage
};
}
return prev;
});
setProgressPercentage(percentage);
}
}
);
loader.await().then(resolved => {
setProgressPercentage(100);
const imageData = new Map<string, string>();
const langPack: Partial<_LanguagePack> = {};
for (const data of resolved)
{
if (data.url.includes('lang/'))
langPack[
data.url
.split('/')
.at(-1)!
.split('.')[0] as keyof _LanguagePack
] = data.resolved as Record<string, any>;
if (data.url.includes('img/'))
imageData.set(
data.url,
data.resolved ?
URL.createObjectURL(new Blob([data.resolved as Blob]))
: 'null'
);
}
setLangPack(langPack);
setImageData(imageData);
waitAsync(500).then(() => setProgressPercentage(() => 101));
});
await waitAsync(500).then(() => setIntroVisible(() => true));
await waitAsync(3000).then(() => setIntroVisible(() => false));
await waitAsync(500).then(() => {
setLogoVisible(() => true);
setIntroFinished(() => true);
});
await waitAsync(animateLogo ? 6500 : 1200)
.then(() => {
setLogoVisible(() => false);
const postIntroAnimSpeed = getPostIntroAnimSpeed(lang ?? '');
if (postIntroAnimSpeed)
document.body.style.setProperty('--anim-playback-rate', postIntroAnimSpeed.toString());
});
await waitAsync(500)
.then(() => setFinishedLoading(() => true));
})();
}, []);
return <div className="rel fw fh flex j-flex-center a-flex-center">
<AnimatePresence>
{
(progressPercentage < 101) &&
<MotionBox
fontFamily={"Jetbrains Mono"}
className="fw abs b0 l0"
exit={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
duration: .5,
easings: [0.88,0, 0.22, 0],
delay: 0.2,
}}
color={"#eee"}
zIndex={1}
>
<Box as={"h1"}>
{progressPercentage >= 100 ?
locale("init-prgs-bar.ready") :
(({ loaded, success }) => <>
<Box as={"span"} color={"#777"}>{progressPercentage}% {locale("init-prgs-bar.loading")} {loaded} / {loadDependencies.length}</Box>
{ loaded !== success && <Box as={"span"} color={"#777"}>&nbsp;({loaded - success} {locale("init-prgs-bar.failed")})</Box> }
</>)(progressData)
}
</Box>
<MotionFlex
flexDir={"row-reverse"}
className="fw rel"
initial={{ width: 0, height: '20px', backgroundColor: '#fff' }}
animate={{ width: (progressPercentage > 100 ? 100 : progressPercentage) + '%' }}
transition={{
duration: .3,
easings: [0.88,-0.07, 0.22, 1.01],
}}
>
{/*<Box as={"h1"} m={"auto 0 auto auto"} mixBlendMode={"exclusion"}></Box>*/}
</MotionFlex>
</MotionBox>
}
{ introVisible && <IntroLogo key="intro-logo"/>}
{(() => {
if (!((logoVisible || progressPercentage < 101) && introFinished))
return null;
const config = { dontAnimateChild: dontAnimate, key: `logo-endfield-${currentLang}` };
switch (currentLang)
{
case 'en':
return <LogoLarge_EN {...config}/>;
case 'cn':
return <LogoLarge_CN {...config}/>;
case 'jp':
return <LogoLarge_JP {...config}/>;
case 'kr':
return <LogoLarge_KR {...config}/>;
}
})()}
</AnimatePresence>
{finishedLoading && progressPercentage === 101 && <>
<Body key="body"/>
<Footer key="footer"/>
<Header key="header"/>
</>}
</div>;
};
export async function getServerSideProps(context: { query: Record<string, string | undefined> })
{
let lang = 'en';
if (AvailableLanguages.includes(context.query.lang ?? ''))
lang = context.query.lang as string;
const langPack = await import(`../lang/${lang}.json`);
return {
props:{
lang,
langPack: JSON.stringify(langPack),
fullIntro: context.query.fullIntro ?? null,
}
};
}
export default Home;