From 545b655d4e5af830ffcaa377241b7ec9fb860b5a Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Tue, 15 Apr 2025 00:35:12 -0500 Subject: [PATCH] feat: slicker loading --- .../HeroSection/components/Options/index.tsx | 2 +- .../components/InitialFogReveal.tsx | 34 ++++ .../components/TankSandbox/index.tsx | 31 ++-- .../Tankopedia/HeroSection/index.tsx | 145 ++++++++++-------- .../src/hooks/useFullscreenAvailability.ts | 4 +- .../website/src/stores/tankopediaEphemeral.ts | 2 + 6 files changed, 140 insertions(+), 78 deletions(-) create mode 100644 packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/InitialFogReveal.tsx diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx index dc251d0f4..cd77b0a59 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/Options/index.tsx @@ -75,7 +75,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) { ); const wireframe = TankopediaPersistent.use((state) => state.wireframe); const opaque = TankopediaPersistent.use((state) => state.opaque); - const fullScreenAvailable = useFullscreenAvailability(); + const fullScreenAvailable = useFullscreenAvailability(true); const developerMode = App.use((state) => state.developerMode); const protagonistTank = Duel.use((state) => state.protagonist.tank); const antagonistGun = Duel.use((state) => state.antagonist.gun); diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/InitialFogReveal.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/InitialFogReveal.tsx new file mode 100644 index 000000000..3714241f5 --- /dev/null +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/InitialFogReveal.tsx @@ -0,0 +1,34 @@ +import { useThree } from '@react-three/fiber'; +import { useEffect } from 'react'; +import type { Fog } from 'three'; +import { fogAnimationTime, fogFar0, fogFar1, forNear0, forNear1 } from '..'; +import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral'; + +export function InitialFogReveal() { + const fog = useThree((state) => state.scene.fog as Fog | null); + const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); + + useEffect(() => { + mutateTankopediaEphemeral((draft) => { + draft.revealed = true; + }); + + if (!fog) return; + + const t0 = Date.now(); + + const interval = setInterval(() => { + const t = (Date.now() - t0) / 1000; + const x = t / fogAnimationTime; + const near = forNear1 + (forNear0 - forNear1) * x; + const far = fogFar1 + (fogFar0 - fogFar1) * x; + + fog.near = near; + fog.far = far; + }, 1000 / 60); + + return () => clearInterval(interval); + }, []); + + return null; +} diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx index e50b155d5..cf87d73d9 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx @@ -28,8 +28,8 @@ import { } from '../../../../Armor/components/StaticArmor'; import { AutoClear } from './components/AutoClear'; import { Controls } from './components/Control'; +import { InitialFogReveal } from './components/InitialFogReveal'; import { Lighting } from './components/Lighting'; -import { ModelLoader } from './components/ModelLoader'; import { SceneProps } from './components/SceneProps'; import { TankModel } from './components/TankModel'; @@ -38,15 +38,15 @@ interface TankSandboxProps { naked?: boolean; } -const near0 = 15; -const far0 = 25; -const near1 = 0; -const far1 = 0; -const animationTime = 0.5; +export const forNear0 = 15; +export const fogFar0 = 25; +export const forNear1 = 0; +export const fogFar1 = 0; +export const fogAnimationTime = 0.5; export const TankSandbox = forwardRef( ({ thicknessRange, naked }, ref) => { - const fog = useRef(new Fog('black', near0, far0)); + const fog = useRef(new Fog('black', forNear1, fogFar1)); const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); const canvas = useRef(null); const hasImprovedVerticalStabilizer = useEquipment(122); @@ -73,11 +73,11 @@ export const TankSandbox = forwardRef( const interval = setInterval((e) => { const t = (Date.now() - t0) / 1000; - const x = t / animationTime; + const x = t / fogAnimationTime; const y = Math.cbrt(-2 * Math.abs(x - 0.5)) + 1; - const near = near0 + (near1 - near0) * y; - const far = far0 + (far1 - far0) * y; + const near = forNear0 + (forNear1 - forNear0) * y; + const far = fogFar0 + (fogFar1 - fogFar0) * y; fog.current.near = near; fog.current.far = far; @@ -86,8 +86,8 @@ export const TankSandbox = forwardRef( if (rawDisplay !== display && x >= 0.5) setDisplay(rawDisplay); if (x >= 1) { - fog.current.near = near0; - fog.current.far = far0; + fog.current.near = forNear0; + fog.current.far = fogFar0; clearInterval(interval); } }, 1000 / 60); @@ -234,7 +234,6 @@ export const TankSandbox = forwardRef( outline: naked ? '1rem red solid' : undefined, }} > - {!naked && } {(display === TankopediaDisplay.Model || (display === TankopediaDisplay.DynamicArmor && @@ -243,7 +242,11 @@ export const TankSandbox = forwardRef( - }> + + {/* Controls within Suspense to allow for frame-perfect start of camera auto-rotate */} + + + {display === TankopediaDisplay.DynamicArmor && } {display === TankopediaDisplay.StaticArmor && ( diff --git a/packages/website/src/components/Tankopedia/HeroSection/index.tsx b/packages/website/src/components/Tankopedia/HeroSection/index.tsx index 1d3d1fdac..759ae643d 100644 --- a/packages/website/src/components/Tankopedia/HeroSection/index.tsx +++ b/packages/website/src/components/Tankopedia/HeroSection/index.tsx @@ -1,5 +1,5 @@ import { TankType } from '@blitzkit/core'; -import { Box, Flex, Heading, Text } from '@radix-ui/themes'; +import { Box, Flex, Heading, Spinner, Text } from '@radix-ui/themes'; import { times } from 'lodash-es'; import { Suspense, useEffect, useMemo, useRef } from 'react'; import { NAVBAR_HEIGHT } from '../../../constants/navbar'; @@ -13,11 +13,11 @@ import type { ThicknessRange } from '../../Armor/components/StaticArmor'; import { classIcons } from '../../ClassIcon'; import { Options } from './components/Options'; import { TankSandbox } from './components/TankSandbox'; -import { TankSandboxLoader } from './components/TankSandboxLoader'; const tankDefinitions = await awaitableTankDefinitions; export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) { + const revealed = TankopediaEphemeral.use((state) => state.revealed); const disturbed = TankopediaEphemeral.use((state) => state.disturbed); const { unwrap } = useLocale(); const canvas = useRef(null); @@ -92,63 +92,6 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) { top={isFullScreen ? '0' : undefined} left={isFullScreen ? '0' : undefined} > - - - - - - - - {unwrap(protagonist.name)} - - - - - - BlitzKit Tankopedia - - - - - {skeleton && } + {/* {skeleton && } */} - }> + @@ -178,6 +121,86 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) { /> + + + + + + + + + + {unwrap(protagonist.name)} + + + + + + BlitzKit Tankopedia + + + ); diff --git a/packages/website/src/hooks/useFullscreenAvailability.ts b/packages/website/src/hooks/useFullscreenAvailability.ts index 9bc68ae20..d31d80728 100644 --- a/packages/website/src/hooks/useFullscreenAvailability.ts +++ b/packages/website/src/hooks/useFullscreenAvailability.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; -export function useFullscreenAvailability() { - const [available, setAvailable] = useState(false); +export function useFullscreenAvailability(assumption = false) { + const [available, setAvailable] = useState(assumption); useEffect(() => { setAvailable(document.fullscreenEnabled); diff --git a/packages/website/src/stores/tankopediaEphemeral.ts b/packages/website/src/stores/tankopediaEphemeral.ts index 0e9c14460..5b6081960 100644 --- a/packages/website/src/stores/tankopediaEphemeral.ts +++ b/packages/website/src/stores/tankopediaEphemeral.ts @@ -97,6 +97,7 @@ export const SHOOTING_RANGE_ZOOM_COEFFICIENTS: Record< interface TankopediaEphemeral { disturbed: boolean; + revealed: boolean; shot?: Shot; shootingRangeZoom: ShootingRangeZoom; skills: Record; @@ -130,6 +131,7 @@ export const TankopediaEphemeral = createContextualStore( return create()( subscribeWithSelector(() => ({ disturbed: false, + revealed: false, shootingRangeZoom: ShootingRangeZoom.Arcade, relativeAgainst: TankopediaRelativeAgainst.Class, editStatic: false,