multi canvas issue

Author: TacticalReaderCreated Mar 30, 2026Updated Mar 30, 2026

the ball.jsx file renders 13 canvas which bad for android users how about this code for ball.jsx import React, { Suspense } from "react"; import { Canvas } from "@react-three/fiber"; import { Decal, Float, OrbitControls, Preload, useTexture, } from "@react-three/drei";

import CanvasLoader from "../Loader";

const Ball = ({ imgUrl, position, ballScale = 2.75 }) => { const [decal] = useTexture([imgUrl]);

return ( <directionalLight position={[0, 0, 0.05]} /> <icosahedronGeometry args={[1, 1]} /> <Decal position={[0, 0, 1]} rotation={[2 * Math.PI, 0, 6.25]} scale={1} map={decal} flatShading /> ); };

const BallsCanvas = ({ technologies }) => { const [isMobile, setIsMobile] = React.useState(false);

React.useEffect(() => { const mediaQuery = window.matchMedia("(max-width: 500px)"); setIsMobile(mediaQuery.matches); const handleMediaQueryChange = (event) => setIsMobile(event.matches); mediaQuery.addEventListener("change", handleMediaQueryChange); return () => mediaQuery.removeEventListener("change", handleMediaQueryChange); }, []);

// Arrange balls in a grid layout const cols = isMobile ? 2 : 5; // balls per row // Slightly tighter spacing on mobile to match the smaller ball scale (1.8 vs 2.75) const spacingX = isMobile ? 5 : 7; const spacingY = isMobile ? 5 : 7;

const totalRows = Math.ceil(technologies.length / cols); const offsetY = ((totalRows - 1) * spacingY) / 2;

const positions = technologies.map((_, index) => { const row = Math.floor(index / cols); const col = index % cols; const itemsInRow = Math.min(cols, technologies.length - row * cols); const rowOffsetX = ((itemsInRow - 1) * spacingX) / 2; const x = col * spacingX - rowOffsetX; const y = -(row * spacingY) + offsetY; return [x, y, 0]; });

// Calculate camera distance based on grid size const fov = 25; const gridWidth = (cols - 1) * spacingX; const gridHeight = (totalRows - 1) * spacingY; // On mobile: derive cameraZ from FOV math so the FULL grid height fits in view. // Formula: cameraZ = (gridHeight/2) / tan(fov/2) with a small margin buffer. const cameraZ = isMobile ? ((gridHeight / 2) / Math.tan((fov / 2) * (Math.PI / 180))) * 1.15 : Math.max(gridWidth, gridHeight) * 1.1 + 10;

return ( <Canvas frameloop='demand' dpr={[1, 2]} gl={{ preserveDrawingBuffer: true }} camera={{ position: [0, 0, cameraZ], fov: fov }} style={{ width: '100%', height: ${totalRows * (isMobile ? 140 : 150)}px }} > <Suspense fallback={}> <directionalLight position={[0, 0, 5]} /> {technologies.map((tech, index) => ( <Ball key={tech.name} imgUrl={tech.icon} position={positions[index]} ballScale={isMobile ? 1.7 : 2.75} /> ))}

  <Preload all />
</Canvas>

); };

// Keep backward-compatible single ball export const BallCanvas = ({ icon }) => { return ( <Canvas frameloop='demand' dpr={[1, 2]} gl={{ preserveDrawingBuffer: true }} > <Suspense fallback={}> <Ball imgUrl={icon} position={[0, 0, 0]} />

  <Preload all />
</Canvas>

); };

export default BallCanvas; export { BallsCanvas }; and after this you just need to update the tech.jsx accordingly

Source: adrianhajdin/project_3D_developer_portfolio