From 1a5e9b37e9665f3ea4c4ff2c6ae09584928f1aba Mon Sep 17 00:00:00 2001 From: ChisatoNishikigi73 Date: Sun, 29 Sep 2024 00:27:12 +0800 Subject: [PATCH 1/5] fix: enhance SVG icon color control. allowing them to better adapt to different themes and design requirements. --- src/components/SvgIcons.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/SvgIcons.tsx b/src/components/SvgIcons.tsx index b143fb0..884459e 100644 --- a/src/components/SvgIcons.tsx +++ b/src/components/SvgIcons.tsx @@ -3,7 +3,8 @@ import React from "react"; export function SvgDefs() { return - @@ -94,17 +95,21 @@ export function SvgDefs() { } -export interface SvgIconProps { +export interface SvgIconProps extends React.SVGProps { className?: React.ComponentProps<"svg">["className"] style?: React.CSSProperties + onClick?: () => void + color?: string } export function TitleArknights(props: SvgIconProps) { - return - + const { color, ...otherProps } = props; + return + } + export function LogoRhodesIsland(props: SvgIconProps) { return @@ -118,7 +123,7 @@ export function CopyrightMini(props: SvgIconProps) { } export function IconArrow(props: SvgIconProps) { - return + return } From 126b96544b15f038fca9ec7ba55b639fcad0d3dd Mon Sep 17 00:00:00 2001 From: ChisatoNishikigi73 Date: Sun, 29 Sep 2024 02:17:53 +0800 Subject: [PATCH 2/5] feat: The world page has been preliminarily improved, but due to the addition of the Init function, it has to be submitted first --- src/pages/_views/03-World.tsx | 170 ++++++++++++++++++++++++---------- 1 file changed, 121 insertions(+), 49 deletions(-) diff --git a/src/pages/_views/03-World.tsx b/src/pages/_views/03-World.tsx index 35b4232..e54b69e 100644 --- a/src/pages/_views/03-World.tsx +++ b/src/pages/_views/03-World.tsx @@ -4,7 +4,17 @@ import { viewIndex } from "../../components/store/rootLayoutStore.ts"; import { directions } from "../../components/store/lineDecoratorStore.ts"; import { IconArrow } from "../../components/SvgIcons.tsx"; -function List() { +const items = [ + {title: "源石", subTitle: "ORIGINIUMS", imageUrl: "/images/03-world/originiums.png", description: '大地被起因不明的天灾四处肆虐,经由天灾卷过的土地上出现了大量的神秘矿物——"源石"。依赖于技术的进步,源石蕴含的能量投入工业后使得文明顺利迈入现代,与此同时,源石本身也催生出"感染者"的存在。'}, + {title: "源石技艺", subTitle: "ORIGINIUM ARTS", imageUrl: "/images/03-world/originium_arts.png", description: "源石技艺的描述..."}, + {title: "整合运动", subTitle: "REUNION", imageUrl: "/images/03-world/reunion.png", description: "整合运动的描述..."}, + {title: "感染者", subTitle: "INFECTED", imageUrl: "/images/03-world/infected.png", description: "感染者的描述..."}, + {title: "移动城邦", subTitle: "NOMADIC CITY", imageUrl: "/images/03-world/nomadic_city.png", description: "移动城邦的描述..."}, + {title: "罗德岛", subTitle: "RHODES ISLAND", imageUrl: "/images/03-world/rhodes_island.png", description: "罗德岛的描述..."}, +]; + +function List({ onItemSelect }: { onItemSelect: (index: number) => void }) { + const [isExiting, setIsExiting] = useState(false); const listRef = useRef(null); const [activeImage, setActiveImage] = useState(null); const [imagePosition, setImagePosition] = useState({ x: 0, y: 0 }); @@ -12,15 +22,6 @@ function List() { const animationFrameRef = useRef(null); const isFirstMove = useRef(true); // 用一个变量来修复首次加载会导致图片位置错误的问题 - const items = [ - {title: "源石", subTitle: "ORIGINIUMS", imageUrl: "/images/03-world/originiums.png"}, - {title: "源石技艺", subTitle: "ORIGINIUM ARTS", imageUrl: "/images/03-world/originium_arts.png"}, - {title: "整合运动", subTitle: "REUNION", imageUrl: "/images/03-world/reunion.png"}, - {title: "感染者", subTitle: "INFECTED", imageUrl: "/images/03-world/infected.png"}, - {title: "移动城邦", subTitle: "NOMADIC CITY", imageUrl: "/images/03-world/nomadic_city.png"}, - {title: "罗德岛", subTitle: "RHODES ISLAND", imageUrl: "/images/03-world/rhodes_island.png"}, - ]; - const handleMouseMove = (e: React.MouseEvent) => { if (listRef.current) { const rect = listRef.current.getBoundingClientRect(); @@ -81,15 +82,32 @@ function List() { }; }, [targetPosition, activeImage]); + const handleItemClick = (index: number) => { + setIsExiting(true); + setTimeout(() => { + onItemSelect(index); + }, 500); // 退出动画时间 + }; + + const itemAnimationDuration = 300; // 每个元素的动画时间(毫秒) + const itemAnimationDelay = 50; // 元素之间的延迟时间(毫秒) + return (
{items.map(({title, subTitle}, index) => ( - + handleItemClick(index)} + isExiting={isExiting} + /> ))} {activeImage && ( void, + isExiting: boolean +}) { const $viewIndex = useStore(viewIndex) const [active, setActive] = useState(false) + const [isVisible, setIsVisible] = useState(false) const itemDom = useRef(null); useEffect(() => { - // TODO: 补全动效 - $viewIndex === 3 - ? itemDom.current!.classList.remove("-translate-x-full", "opacity-0") - : itemDom.current!.classList.add("-translate-x-full", "opacity-0") - }, [$viewIndex]); + if ($viewIndex === 3) { + const timer = setTimeout(() => { + setIsVisible(true); + }, delay); + return () => clearTimeout(timer); + } else { + setIsVisible(false); + } + }, [$viewIndex, delay]); return (
setActive(true)} onMouseLeave={() => setActive(false)} + onClick={onClick} >
void, + onPrevious: () => void, + onNext: () => void +}) { return <>
- {title} + {item.title}
- {subTitle} + {item.subTitle}
- {description} + {item.description}
+ className="w-8 portrait:w-[1.25rem] box-content px-8 py-4 portrait:p-4 block absolute top-[48.1481481481%] portrait:top-[35.2323838081%] left-[6.625rem] portrait:left-[3.5rem] rotate-180 opacity-70 hover:opacity-100 z-[2] cursor-pointer transition-opacity duration-300" + onClick={onPrevious} + /> + className="w-8 portrait:w-[1.25rem] box-content px-8 py-4 portrait:p-4 block absolute top-[48.1481481481%] portrait:top-[35.2323838081%] left-[95.5rem] portrait:left-auto portrait:right-[9.5rem] opacity-70 hover:opacity-100 z-[2] cursor-pointer transition-opacity duration-300" + onClick={onNext} + />
-
-
-
-
-
-
-
+ {items.map((_, index) => ( +
+ ))}
+ style={{transition: "color .3s, background-color .3s, transform .6s, opacity .6s, visibility .6s"}} + onClick={onBack} + >
返回
@@ -225,6 +257,7 @@ function Details({title, subTitle, description}: { title: string, subTitle: stri } +// TODO: 把颗粒集中 function AshParticles({ count = 20 }: { count?: number }) { const canvasRef = useRef(null); @@ -319,6 +352,7 @@ function AshParticles({ count = 20 }: { count?: number }) { export default function World() { const $viewIndex = useStore(viewIndex) const world = useRef(null) + const [selectedItemIndex, setSelectedItemIndex] = useState(null); useEffect(() => { if ($viewIndex === 3) { @@ -329,18 +363,56 @@ export default function World() { } }, [$viewIndex]) - return
+ const handleItemSelect = (index: number) => { + setSelectedItemIndex(index); + }; + + const handleBack = () => { + setSelectedItemIndex(null); + }; + + const handlePrevious = () => { + setSelectedItemIndex((prevIndex) => + prevIndex === null ? null : (prevIndex - 1 + items.length) % items.length + ); + }; + + const handleNext = () => { + setSelectedItemIndex((prevIndex) => + prevIndex === null ? null : (prevIndex + 1) % items.length + ); + }; + + return
- + {selectedItemIndex === null ? ( + + ) : ( +
+ )}
WORLD
-
+
+
+ {items.map((_, index) => ( +
handleItemSelect(index)} + /> + ))} +
+
} From 00ed5d63d54953e1834d89ccf410e124e678a9cb Mon Sep 17 00:00:00 2001 From: ChisatoNishikigi73 Date: Sun, 29 Sep 2024 03:33:13 +0800 Subject: [PATCH 3/5] feat: add left line --- src/components/LineDecorator.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/LineDecorator.tsx b/src/components/LineDecorator.tsx index 94ea403..7b5c06a 100644 --- a/src/components/LineDecorator.tsx +++ b/src/components/LineDecorator.tsx @@ -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
+
} From 8ba2cace7a93ce52cb66eb7a07ffd57b188cb2fc Mon Sep 17 00:00:00 2001 From: ChisatoNishikigi73 Date: Sun, 29 Sep 2024 04:47:00 +0800 Subject: [PATCH 4/5] fix: Fix issue of redirecting to #index when reloading the page --- src/pages/_views/RootPageViews.tsx | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/pages/_views/RootPageViews.tsx b/src/pages/_views/RootPageViews.tsx index 37e3d03..edd1230 100644 --- a/src/pages/_views/RootPageViews.tsx +++ b/src/pages/_views/RootPageViews.tsx @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useRef} from "react"; +import {useCallback, useEffect, useRef, useState} from "react"; import {useStore} from "@nanostores/react"; import {viewIndex, viewIndexSetNext, viewIndexSetPrev} from "../../components/store/rootLayoutStore.ts"; import arknightsConfig from "../../../arknights.config.tsx"; @@ -13,7 +13,7 @@ import More from "./05-More.tsx"; export default function RootPageViews() { const $viewIndex = useStore(viewIndex) -//// 首次挂载组件通过当前锚点设置 viewIndex + //// 首次挂载组件通过当前锚点设置 viewIndex useEffect(() => { const HASH = location.hash.split("#")[1]; const INDEX = arknightsConfig.navbar.items.findIndex(item => @@ -21,14 +21,18 @@ export default function RootPageViews() { viewIndex.set(INDEX === -1 ? 0 : INDEX) }, []) -//// 响应移动端上下滑动手势 + //// 响应移动端上下滑动手势 const startTouchY = useRef(0) - const handleTouchStart = useCallback((event: TouchEvent) => startTouchY.current = event.touches[0].clientY, []) + const handleTouchStart = useCallback((event: TouchEvent) => { + startTouchY.current = event.touches[0].clientY + }, []) const handleTouchEnd = useCallback((event: TouchEvent) => { const diffY = startTouchY.current - event.changedTouches[0].clientY - if (Math.abs(diffY) > 160) diffY > 0 ? viewIndexSetNext() : viewIndexSetPrev() + if (Math.abs(diffY) > 160) { + diffY > 0 ? viewIndexSetNext() : viewIndexSetPrev() + } }, [viewIndexSetNext, viewIndexSetPrev]) useEffect(() => { @@ -43,7 +47,7 @@ export default function RootPageViews() { } }, [handleTouchStart, handleTouchEnd]) -//// 响应鼠标滚轮 + //// 响应鼠标滚轮 // 上次鼠标滚轮使用时间戳 const lastScrollTime = useRef(0); @@ -63,11 +67,12 @@ export default function RootPageViews() { } } - document.getElementById("root-page-views")!.addEventListener("wheel", handleScroll) - return () => document.getElementById("root-page-views")!.removeEventListener("wheel", handleScroll); + const rootElement = document.getElementById("root-page-views") + rootElement!.addEventListener("wheel", handleScroll) + return () => rootElement!.removeEventListener("wheel", handleScroll); }, [$viewIndex]) -//// 监听锚点链接改变修改 viewIndex + //// 监听锚点链接改变修改 viewIndex useEffect(() => { const handleHashChange = (hce: HashChangeEvent) => { const index: number = arknightsConfig.navbar.items.findIndex(item => @@ -79,7 +84,7 @@ export default function RootPageViews() { return () => window.removeEventListener("hashchange", handleHashChange) }, []) - return [Index, Information, Operator, World, Media, More].map((Element, index) => - ) + + ) } From 70ad8cb016f409cfe0b1c69c69273de47a9432b0 Mon Sep 17 00:00:00 2001 From: ChisatoNishikigi73 Date: Sun, 29 Sep 2024 04:55:04 +0800 Subject: [PATCH 5/5] feat: Add initialization page; now displays before entering main page --- src/components/Init.tsx | 206 ++++++++++++++++++++++++ src/components/store/rootLayoutStore.ts | 2 + src/layouts/RootLayout.astro | 8 +- src/pages/_views/00-Index.tsx | 6 +- src/pages/_views/01-Information.tsx | 10 +- src/pages/_views/02-Operator.tsx | 28 +++- src/pages/_views/03-World.tsx | 91 ++++++----- src/pages/_views/04-Media.tsx | 26 ++- src/pages/_views/05-More.tsx | 26 ++- 9 files changed, 344 insertions(+), 59 deletions(-) create mode 100644 src/components/Init.tsx diff --git a/src/components/Init.tsx b/src/components/Init.tsx new file mode 100644 index 0000000..7947963 --- /dev/null +++ b/src/components/Init.tsx @@ -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>(new Set()); + const [loadedResources, setLoadedResources] = useState>(new Set()); + const observerRef = useRef(null); + const [isObserving, setIsObserving] = useState(true); + const listRef = useRef(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 ( +
+ {/* 上边线 */} +
+ {/* 右边线 */} +
+ +
+
+ +
+ +
+ +
+ +
+
+
+ © YUE_PLUS +
+
+
+
+
+
+
+
+
+
+ + {`LOADING - ${Math.round(progress)}%`} +
+
+ ARKNIGHTS + // + https://github.com/Yue-plus/astro-arknights +
+
+
+
+
+ +
+
+ +
+

已加载成功的资源:

+
    + {Array.from(loadedResources).map((resource, index) => ( +
  • {resource}
  • + ))} +
+

+ {$isInitialized ? "背景图片加载成功" : "正在加载背景图片..."} +

+

加载进度:{Math.round(progress)}%

+
+
+ ); +} \ No newline at end of file diff --git a/src/components/store/rootLayoutStore.ts b/src/components/store/rootLayoutStore.ts index ad0a6a0..b5846d7 100644 --- a/src/components/store/rootLayoutStore.ts +++ b/src/components/store/rootLayoutStore.ts @@ -16,3 +16,5 @@ export function viewIndexSetPrev() { export const isNavMenuOpen = atom(false) export const isToolBoxOpen = atom(false) export const isOwnerInfoOpen = atom(false) +export const isInitialized = atom(false) +export const readyToTouch = atom(false) // TODO: 和isInitialized合并 diff --git a/src/layouts/RootLayout.astro b/src/layouts/RootLayout.astro index dd98735..79cba83 100644 --- a/src/layouts/RootLayout.astro +++ b/src/layouts/RootLayout.astro @@ -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 @@ -36,7 +37,8 @@ const {lang, title, description, subNavigationItems} = Astro.props -
+ +
@@ -44,7 +46,7 @@ const {lang, title, description, subNavigationItems} = Astro.props
-
- +
+ diff --git a/src/pages/_views/00-Index.tsx b/src/pages/_views/00-Index.tsx index faac633..7335480 100644 --- a/src/pages/_views/00-Index.tsx +++ b/src/pages/_views/00-Index.tsx @@ -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 { - 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
diff --git a/src/pages/_views/01-Information.tsx b/src/pages/_views/01-Information.tsx index 4d65642..f73a87e 100644 --- a/src/pages/_views/01-Information.tsx +++ b/src/pages/_views/01-Information.tsx @@ -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 @@ -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
diff --git a/src/pages/_views/02-Operator.tsx b/src/pages/_views/02-Operator.tsx index 9ee6aba..50c5bfa 100644 --- a/src/pages/_views/02-Operator.tsx +++ b/src/pages/_views/02-Operator.tsx @@ -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
-

TODO: Operator

-
+ 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 ( +
+
+

TODO: Operator

+
+ {/* TODO: Operator */} +
+ ); } diff --git a/src/pages/_views/03-World.tsx b/src/pages/_views/03-World.tsx index e54b69e..ef31acf 100644 --- a/src/pages/_views/03-World.tsx +++ b/src/pages/_views/03-World.tsx @@ -1,8 +1,9 @@ import React, { useEffect, useRef, useState } from "react"; import { useStore } from "@nanostores/react"; -import { viewIndex } from "../../components/store/rootLayoutStore.ts"; +import { viewIndex, readyToTouch } from "../../components/store/rootLayoutStore.ts"; import { directions } from "../../components/store/lineDecoratorStore.ts"; import { IconArrow } from "../../components/SvgIcons.tsx"; +import PortraitBottomGradientMask from "../../components/PortraitBottomGradientMask"; const items = [ {title: "源石", subTitle: "ORIGINIUMS", imageUrl: "/images/03-world/originiums.png", description: '大地被起因不明的天灾四处肆虐,经由天灾卷过的土地上出现了大量的神秘矿物——"源石"。依赖于技术的进步,源石蕴含的能量投入工业后使得文明顺利迈入现代,与此同时,源石本身也催生出"感染者"的存在。'}, @@ -31,9 +32,14 @@ function List({ onItemSelect }: { onItemSelect: (index: number) => void }) { const imgWidth = 1024; const imgHeight = 1024; + // TODO: 一个奇怪的偏移,需要更好的方法 + const imgOffsetX = 350; + const imgOffsetY = 0; + const imgOffsetXPercentage = 75; + const imgOffsetYPercentage = 0; const newPosition = { - x: x - imgWidth / 2 + 345, - y: y - imgHeight / 2 + x: x - imgWidth / 2 + (imgOffsetX * imgOffsetXPercentage / 100), + y: y - imgHeight / 2 + (imgOffsetY * imgOffsetYPercentage / 100) }; if (isFirstMove.current) { @@ -95,7 +101,7 @@ function List({ onItemSelect }: { onItemSelect: (index: number) => void }) { return (
@@ -351,17 +357,18 @@ function AshParticles({ count = 20 }: { count?: number }) { export default function World() { const $viewIndex = useStore(viewIndex) + const $readyToTouch = useStore(readyToTouch) const world = useRef(null) const [selectedItemIndex, setSelectedItemIndex] = useState(null); + const [active, setActive] = useState(false); useEffect(() => { - if ($viewIndex === 3) { + const isActive = $viewIndex === 3 && $readyToTouch; + if (isActive) { directions.set({top: false, right: true, bottom: true, left: false}) - world.current!.classList.remove("opacity-0") - } else { - world.current!.classList.add("opacity-0") } - }, [$viewIndex]) + setActive(isActive); + }, [$viewIndex, $readyToTouch]) const handleItemSelect = (index: number) => { setSelectedItemIndex(index); @@ -383,36 +390,42 @@ export default function World() { ); }; - return
-
- -
- {selectedItemIndex === null ? ( - - ) : ( -
- )} -
- WORLD -
-
-
- {items.map((_, index) => ( -
handleItemSelect(index)} - /> - ))} + return ( +
+
+ +
+ {selectedItemIndex === null ? ( + + ) : ( +
+ )} +
+ WORLD +
+
+
+ {items.map((_, index) => ( +
handleItemSelect(index)} + /> + ))} +
+
-
+ ) } diff --git a/src/pages/_views/04-Media.tsx b/src/pages/_views/04-Media.tsx index a0e09aa..a774fd3 100644 --- a/src/pages/_views/04-Media.tsx +++ b/src/pages/_views/04-Media.tsx @@ -1,7 +1,25 @@ -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 Media() { - return
-

TODO: Media

-
+ const $viewIndex = useStore(viewIndex); + const $readyToTouch = useStore(readyToTouch); + const [active, setActive] = useState(false); + + useEffect(() => { + const isActive = $viewIndex === 4 && $readyToTouch; + if (isActive) directions.set({ top: false, right: true, bottom: true, left: false }); + setActive(isActive); + }, [$viewIndex, $readyToTouch]); + + return ( +
+

TODO: Media

+
+ ); } diff --git a/src/pages/_views/05-More.tsx b/src/pages/_views/05-More.tsx index aced579..8ff0919 100644 --- a/src/pages/_views/05-More.tsx +++ b/src/pages/_views/05-More.tsx @@ -1,7 +1,25 @@ -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 More() { - return
-

TODO: More

-
+ const $viewIndex = useStore(viewIndex); + const $readyToTouch = useStore(readyToTouch); + const [active, setActive] = useState(false); + + useEffect(() => { + const isActive = $viewIndex === 5 && $readyToTouch; + if (isActive) directions.set({ top: false, right: false, bottom: true, left: false }); + setActive(isActive); + }, [$viewIndex, $readyToTouch]); + + return ( +
+

TODO: More

+
+ ); }