Skip to content

Commit

Permalink
Merge pull request #2 from ChisatoNishikigi73/main
Browse files Browse the repository at this point in the history
Add initialization page and fix various bugs
  • Loading branch information
Yue-plus authored Sep 29, 2024
2 parents 310223d + 70ad8cb commit 468aa9e
Show file tree
Hide file tree
Showing 12 changed files with 472 additions and 103 deletions.
206 changes: 206 additions & 0 deletions src/components/Init.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import React, { useEffect, useState, useCallback, useRef } from "react";
import { IconDblArrow, TitleArknights } from "../components/SvgIcons";
import { useStore } from "@nanostores/react";
import { isInitialized, readyToTouch } from "../components/store/rootLayoutStore";

export function Init() {
const $isInitialized = useStore(isInitialized);
const $readyToTouch = useStore(readyToTouch);
const [progress, setProgress] = useState(0);
const [isHidden, setIsHidden] = useState(false);
const [isPortrait, setIsPortrait] = useState(false);
const [isFadingOut, setIsFadingOut] = useState(false);
const [isComplete, setIsComplete] = useState(false);
const commonColor = "rgb(164,164,164)";
const loadingColorText = "#61cefa";
const loadingColor = "#61cefa";
const [loadingResources, setLoadingResources] = useState<Set<string>>(new Set());
const [loadedResources, setLoadedResources] = useState<Set<string>>(new Set());
const observerRef = useRef<PerformanceObserver | null>(null);
const [isObserving, setIsObserving] = useState(true);
const listRef = useRef<HTMLDivElement>(null);

//TODO: 继续完善init, 目前的做法是等待/images/index-bg.jpg加载完毕
const incrementProgress = useCallback(() => {
setProgress(prevProgress => Math.min(prevProgress + 0.5, 80));
}, []);

const stopObserving = useCallback(() => {
if (observerRef.current) {
observerRef.current.disconnect();
observerRef.current = null;
setIsObserving(false);
// console.log("停止资源加载监控");
}
}, []);

useEffect(() => {
if (!isObserving) return; // 如果已经停止观察,不再创建新的观察器

const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.entryType === 'resource') {
const resourceName = (entry as PerformanceResourceTiming).name;
if (!loadedResources.has(resourceName)) {
// console.log("资源加载成功:", resourceName);
setLoadedResources(prev => new Set(prev).add(resourceName));
incrementProgress();

if (resourceName.endsWith('/images/index-bg.jpg')) {
isInitialized.set(true);
stopObserving();
// console.log("背景图片加载成功,停止监控");
}
}
}
});
});

observerRef.current = observer;
observer.observe({ entryTypes: ['resource'] });

// 检查已经加载成功的资源
const checkExistingResources = () => {
const resources = performance.getEntriesByType('resource');
resources.forEach((entry) => {
if (!loadedResources.has(entry.name)) {
// console.log("已加载成功的资源:", entry.name);
setLoadedResources(prev => new Set(prev).add(entry.name));
incrementProgress();
if (entry.name.endsWith('/images/index-bg.jpg')) {
isInitialized.set(true);
stopObserving();
// console.log("背景图片已加载成功,停止监控");
}
}
});
};

checkExistingResources();

return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, [incrementProgress, loadedResources, stopObserving, isObserving]);

useEffect(() => {
let interval: NodeJS.Timeout;
if ($isInitialized && progress < 100) {
interval = setInterval(() => {
setProgress(prevProgress => {
const newProgress = prevProgress + 100;
if (newProgress >= 100) {
clearInterval(interval);
return 100;
}
return newProgress;
});
}, 50);
}
return () => clearInterval(interval);
}, [$isInitialized, progress]);

useEffect(() => {
if (progress >= 100 && !isComplete) {
setIsComplete(true);
setTimeout(() => {
readyToTouch.set(true);
setIsFadingOut(true);
setTimeout(() => {
setIsHidden(true);
}, 800);
}, 500);
}
}, [progress, isComplete]);

useEffect(() => {
if (listRef.current) {
listRef.current.scrollTop = listRef.current.scrollHeight;
}
}, [loadedResources]);

if (isHidden) return null;

return (
<div className={`fixed inset-0 bg-[#272727] flex flex-col items-center justify-center z-50 font-benderBold transition-opacity duration-1000 ${isFadingOut ? 'opacity-0' : 'opacity-100'}`}>
{/* 上边线 */}
<div className={`absolute left-0 right-0 h-[0.05vw] bg-[#686767] transition-all duration-1000 ease-in-out ${
isFadingOut ? 'top-[-5vw]' : 'top-[5vw]'
}`} />
{/* 右边线 */}
<div className={`absolute top-0 bottom-0 w-[0.05vw] bg-[#686767] transition-all duration-1000 ease-in-out ${
isFadingOut ? 'right-[-5vw]' : 'right-[5vw]'
}`} />

<div className="flex flex-col items-center justify-center h-full w-full">
<div className="flex-grow" />

<div className="flex items-center justify-center mb-[2vw]">
<TitleArknights
className="w-[13vw] h-[17vw] max-w-full"
color={commonColor}
/>
</div>

<div className="flex-grow" />

<div className="w-full max-w-[90vw] px-[2vw] absolute" style={{ top: '75%' }}>
<div className={`flex items-start ${isPortrait ? 'flex-col' : ''}`}>
<div
className={`whitespace-nowrap ${
isPortrait
? 'fixed bottom-[1%] left-[1%] text-[10px]' ////idk,反正就是text-[1vw]
: 'mr-[15vw] text-[1.2vw]'
}`}
style={{ color: commonColor }}
>
<span>© YUE_PLUS</span>
</div>
<div className={`flex-grow ${isPortrait ? 'w-full' : ''} pl-[5vw] pr-[5.5vw]`}>
<div className="relative h-[0.3vw] flex items-center" style={{ backgroundColor: 'transparent' }}>
<div className="absolute left-0 w-[0.3vw] h-[0.3vw]" style={{ backgroundColor: commonColor }}></div>
<div className="absolute right-0 w-[0.3vw] h-[0.3vw]" style={{ backgroundColor: commonColor }}></div>
<div className="absolute top-[0.1vw] left-0 right-0 h-[0.1vw]" style={{ backgroundColor: commonColor }}></div>
<div
className="absolute top-0 left-0 h-[0.3vw] transition-all duration-300 ease-linear"
style={{ width: `${progress}%`, backgroundColor: loadingColor }}
/>
</div>
<div className="flex justify-between items-center mt-[0.8vw]">
<div className="flex items-center text-[0.8vw]" style={{ color: loadingColorText }}>
<IconDblArrow className="w-[0.8vw] h-[0.8vw] mr-[0.4vw]" color={loadingColorText} />
<span>{`LOADING - ${Math.round(progress)}%`}</span>
</div>
<div className={`flex items-center text-[0.8vw]`} style={{ color: commonColor }}>
<span>ARKNIGHTS</span>
<span className="mx-[0.8vw]">//</span>
<span>https://github.com/Yue-plus/astro-arknights</span>
</div>
</div>
</div>
</div>
</div>

<div className="flex-grow" />
</div>

<div
ref={listRef}
className="absolute bottom-[2vw] left-[2vw] text-[0.8vw] text-white max-h-[20vh] overflow-y-auto opacity-50"
>
<h3 className="mb-[0.5vw]">已加载成功的资源:</h3>
<ul>
{Array.from(loadedResources).map((resource, index) => (
<li key={index} className="truncate max-w-[30vw]">{resource}</li>
))}
</ul>
<p className="mt-[0.5vw]">
{$isInitialized ? "背景图片加载成功" : "正在加载背景图片..."}
</p>
<p>加载进度:{Math.round(progress)}%</p>
</div>
</div>
);
}
4 changes: 3 additions & 1 deletion src/components/LineDecorator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ export default function LineDecorator() {
base + (right ? "right-[14.75rem] portrait:right-[5.75rem] " : "-right-px ") + "w-px h-full top-0", [right])
const bottomClassName = useMemo(() =>
base + (bottom ? "bottom-[11.25rem] portrait:bottom-[12rem] " : "-bottom-px ") + "w-full h-px", [bottom])

const leftClassName = useMemo(() =>
base + (left ? "left-[14.75rem] portrait:left-[5.75rem] " : "-left-px ") + "w-px h-full top-0", [left])
return <div id="line-decorator"
className="w-full h-full absolute top-0 left-0 z-[3] pointer-events-none overflow-hidden">
<div className={topClassName}/>
<div className={rightClassName}/>
<div className={bottomClassName}/>
<div className={leftClassName}/>
</div>
}
15 changes: 10 additions & 5 deletions src/components/SvgIcons.tsx

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/components/store/rootLayoutStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export function viewIndexSetPrev() {
export const isNavMenuOpen = atom<boolean>(false)
export const isToolBoxOpen = atom<boolean>(false)
export const isOwnerInfoOpen = atom<boolean>(false)
export const isInitialized = atom(false)
export const readyToTouch = atom(false) // TODO: 和isInitialized合并
8 changes: 5 additions & 3 deletions src/layouts/RootLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {Menu} from "../components/header/NavMenu"
import ToolBox from "../components/ToolBox"
import OwnerInfo from "../components/OwnerInfo"
import {SvgDefs} from "../components/SvgIcons"
import {Init} from "../components/Init";
interface Props {
lang?: string
Expand Down Expand Up @@ -36,15 +37,16 @@ const {lang, title, description, subNavigationItems} = Astro.props
<ResponsiveFontSize/>
</head>
<body class="w-full h-full text-white bg-black m-0 overflow-hidden">
<div class="w-full h-full relative bg-layout bg-center bg-cover bg-no-repeat">
<Init client:load />
<div class="w-full h-full relative bg-layout bg-center bg-cover bg-no-repeat">
<div class="relative w-full h-full m-auto max-w-[180rem]">
<Header/>
<slot/>
<Menu client:only="react" {...{subNavigationItems}}/>
<ToolBox client:load/>
<OwnerInfo client:load/>
</div>
</div>
<SvgDefs/>
</div>
<SvgDefs/>
</body>
</html>
6 changes: 4 additions & 2 deletions src/pages/_views/00-Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {viewIndex} from "../../components/store/rootLayoutStore.ts"
import {directions} from "../../components/store/lineDecoratorStore"
import arknightsConfig from "../../../arknights.config.tsx";
import PortraitBottomGradientMask from "../../components/PortraitBottomGradientMask";
import {readyToTouch} from "../../components/store/rootLayoutStore.ts"

function HeroActionButton({icon, label, subLabel, target, href, className}: HeroActionButtonProps) {
return <a target={target ?? "_blank"} href={href}
Expand All @@ -20,13 +21,14 @@ function HeroActionButton({icon, label, subLabel, target, href, className}: Hero
export default function Index() {
const {title, subtitle, url, copyright} = arknightsConfig.rootPage.INDEX
const $viewIndex = useStore(viewIndex)
const $readyToTouch = useStore(readyToTouch)
const [active, setActive] = useState($viewIndex === 0)

useEffect(() => {
const isActive = $viewIndex === 0
const isActive = $viewIndex === 0 && $readyToTouch
if (isActive) directions.set({top: false, right: true, bottom: true, left: false})
setActive(isActive)
}, [$viewIndex])
}, [$viewIndex, $readyToTouch])

return <div className={"w-[100vw] max-w-[180rem] h-full absolute top-0 right-0 bottom-0 left-0 z-[2]"
+ " transition-opacity duration-100"}>
Expand Down
10 changes: 7 additions & 3 deletions src/pages/_views/01-Information.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {IconArrow} from "../../components/SvgIcons"
import type {BreakingNewsItemProps} from "../../_types/RootPageViews.ts"
import arknightsConfig from "../../../arknights.config.tsx";
import {directions} from "../../components/store/lineDecoratorStore.ts"
import {viewIndex} from "../../components/store/rootLayoutStore.ts"
import {viewIndex, readyToTouch} from "../../components/store/rootLayoutStore.ts"

const base = import.meta.env.BASE_URL

Expand Down Expand Up @@ -199,10 +199,14 @@ function SwiperScrollbar() {
export default function Information() {
const [swiperIndex, setSwiperIndex] = useState(0)
const $viewIndex = useStore(viewIndex)
const $readyToTouch = useStore(readyToTouch)
const [active, setActive] = useState($viewIndex === 1)

useEffect(() => {
if ($viewIndex === 1) directions.set({top: true, right: true, bottom: false, left: false})
}, [$viewIndex])
const isActive = $viewIndex === 1 && $readyToTouch
if (isActive) directions.set({top: true, right: true, bottom: false, left: false})
setActive(isActive)
}, [$viewIndex, $readyToTouch])

return <div
className="w-[100vw] max-w-[180rem] h-full absolute top-0 right-0 bottom-0 left-auto transition-opacity duration-100">
Expand Down
28 changes: 24 additions & 4 deletions src/pages/_views/02-Operator.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { useStore } from "@nanostores/react";
import { viewIndex, readyToTouch } from "../../components/store/rootLayoutStore.ts";
import { directions } from "../../components/store/lineDecoratorStore";

export default function Operator() {
return <div className="w-full h-full" style={{backgroundColor: "darkgrey"}}>
<h1 className="text-9xl absolute top-1/4 left-1/4">TODO: Operator</h1>
</div>
const $viewIndex = useStore(viewIndex);
const $readyToTouch = useStore(readyToTouch);
const [active, setActive] = useState(false);

useEffect(() => {
const isActive = $viewIndex === 2 && $readyToTouch;
if (isActive) {
directions.set({ top: true, right: true, bottom: true, left: false });
}
setActive(isActive);
}, [$viewIndex, $readyToTouch]);

return (
<div className={`w-[100vw] max-w-[180rem] h-full absolute top-0 right-0 bottom-0 left-auto transition-all duration-1000 ${active ? 'opacity-100 visible' : 'opacity-0 invisible'}`}>
<div className="w-full h-full" style={{backgroundColor: "darkgrey"}}>
<h1 className="text-9xl absolute top-1/4 left-1/4">TODO: Operator</h1>
</div>
{/* TODO: Operator */}
</div>
);
}
Loading

0 comments on commit 468aa9e

Please sign in to comment.