diff --git a/packages/website/src/components/GuessRenderer.tsx b/packages/website/src/components/GuessRenderer.tsx
index 16403e519..a7f091924 100644
--- a/packages/website/src/components/GuessRenderer.tsx
+++ b/packages/website/src/components/GuessRenderer.tsx
@@ -1,6 +1,6 @@
-import { Canvas } from '@react-three/fiber';
import { GuessEphemeral } from '../stores/guessEphemeral';
import { TankopediaDisplay } from '../stores/tankopediaPersistent/constants';
+import { SmartCanvas } from './SmartCanvas';
import { Controls } from './Tankopedia/HeroSection/components/TankSandbox/components/Control';
import { Lighting } from './Tankopedia/HeroSection/components/TankSandbox/components/Lighting';
import { TankModel } from './Tankopedia/HeroSection/components/TankSandbox/components/TankModel';
@@ -13,7 +13,7 @@ export function GuessRenderer() {
const isRevealed = guessState !== null;
return (
-
-
+
);
}
diff --git a/packages/website/src/components/SmartCanvas.tsx b/packages/website/src/components/SmartCanvas.tsx
new file mode 100644
index 000000000..fc2a24d07
--- /dev/null
+++ b/packages/website/src/components/SmartCanvas.tsx
@@ -0,0 +1,20 @@
+import { Canvas, type CanvasProps } from '@react-three/fiber';
+import { forwardRef, useImperativeHandle, useRef } from 'react';
+import { useOnScreen } from '../hooks/useOnScreen';
+
+export const SmartCanvas = forwardRef(
+ (props, ref) => {
+ const canvas = useRef(null);
+ const onScreen = useOnScreen(canvas);
+
+ useImperativeHandle(ref, () => canvas.current!, []);
+
+ return (
+
+ );
+ },
+);
diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx
index 74a3b4035..5207a9395 100644
--- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx
+++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/CanvasContent.tsx
@@ -1,4 +1,3 @@
-import { invalidate } from '@react-three/fiber';
import type { QuicklimeEvent } from 'quicklime';
import { useEffect, useRef } from 'react';
import type { Group } from 'three';
@@ -19,8 +18,6 @@ export function CanvasContent() {
if (!gun.current) return;
gun.current.rotation.x = event.data.pitch;
-
- invalidate();
}
modelTransformEvent.on(handleModelTransformEvent);
diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/FlexibilityCanvas.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/FlexibilityCanvas.tsx
index 9a91b910c..43ce564b2 100644
--- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/FlexibilityCanvas.tsx
+++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Characteristics/components/GunFlexibilityVisualizer/components/FlexibilityCanvas.tsx
@@ -1,19 +1,12 @@
-import { Canvas } from '@react-three/fiber';
+import { SmartCanvas } from '../../../../../../../SmartCanvas';
import { CameraPositioner, zoom } from './CameraPositioner';
import { CanvasContent } from './CanvasContent';
export function FlexibilityCanvas() {
return (
-
+
);
}
diff --git a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Control.tsx b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Control.tsx
index 41e0e2c28..b29cc2bcb 100644
--- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Control.tsx
+++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/components/Control.tsx
@@ -1,6 +1,6 @@
import { I_HAT, J_HAT } from '@blitzkit/core';
import { OrbitControls } from '@react-three/drei';
-import { invalidate, useThree } from '@react-three/fiber';
+import { invalidate, useFrame, useThree } from '@react-three/fiber';
import { useEffect, useRef } from 'react';
import { PerspectiveCamera, Vector3 } from 'three';
import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
@@ -90,32 +90,16 @@ export function Controls({
-13,
).multiplyScalar(distanceScale);
- useEffect(() => {
- if (!orbitControls.current || !doAutoRotate) return;
+ const t0 = useRef(Date.now() / 1000);
+ useFrame(() => {
+ const now = Date.now() / 1000;
+ const t = now - t0.current;
- const t0 = Date.now() / 1000;
- let cancel = false;
-
- function frame() {
- if (!cancel) requestAnimationFrame(frame);
-
- const now = Date.now() / 1000;
- const t = now - t0;
-
- camera.position
- .copy(inspectModeInitialPosition)
- .applyAxisAngle(I_HAT, (Math.PI / 32) * Math.sin(t / 9))
- .applyAxisAngle(J_HAT, (-Math.PI / 16) * Math.sin(t / 7));
-
- invalidate();
- }
-
- frame();
-
- return () => {
- cancel = true;
- };
- }, [doAutoRotate, camera]);
+ camera.position
+ .copy(inspectModeInitialPosition)
+ .applyAxisAngle(I_HAT, (Math.PI / 32) * Math.sin(t / 9))
+ .applyAxisAngle(J_HAT, (-Math.PI / 16) * Math.sin(t / 7));
+ });
useEffect(() => {
const unsubscribeTankopediaEphemeral = tankopediaEphemeralStore.subscribe(
@@ -229,8 +213,6 @@ export function Controls({
}, [camera, protagonistTank.id, antagonistTank.id]);
useEffect(() => {
- let isInitialIteration = true;
-
function handleWheel(event: WheelEvent) {
event.preventDefault();
}
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 49a314001..f213ceb71 100644
--- a/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx
+++ b/packages/website/src/components/Tankopedia/HeroSection/components/TankSandbox/index.tsx
@@ -1,5 +1,5 @@
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
-import { Canvas, invalidate } from '@react-three/fiber';
+import { invalidate } from '@react-three/fiber';
import {
forwardRef,
Suspense,
@@ -13,7 +13,6 @@ import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimi
import { modelTransformEvent } from '../../../../../core/blitzkit/modelTransform';
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
import { useEquipment } from '../../../../../hooks/useEquipment';
-import { useOnScreen } from '../../../../../hooks/useOnScreen';
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
import { Duel } from '../../../../../stores/duel';
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
@@ -26,6 +25,7 @@ import {
StaticArmor,
type ThicknessRange,
} from '../../../../Armor/components/StaticArmor';
+import { SmartCanvas } from '../../../../SmartCanvas';
import { AutoClear } from './components/AutoClear';
import { Controls } from './components/Control';
import { InitialFogReveal } from './components/InitialFogReveal';
@@ -65,7 +65,6 @@ export const TankSandbox = forwardRef(
const hideTankModelUnderArmor = TankopediaPersistent.use(
(state) => state.hideTankModelUnderArmor,
);
- const onScreen = useOnScreen(canvas);
useEffect(() => {
if (rawDisplay === display) return;
@@ -204,10 +203,9 @@ export const TankSandbox = forwardRef(
}, [display]);
return (
-
+
);
},
);