#How to make my react three fiber website responsive

56 messages · Page 1 of 1 (latest)

green furnace
#

Pretty much the title, On my pc ~ it looks perfect but when i view it on my mobile the camera zooms in on the object and it doesn't look as it looks on desktop

What should i do to make sure my scene looks almost similar on all devices. My canvas is covering the entire body and everything happens in canvas only

elfin gale
#

maybe do a if statement screen lower than 765px to decrease the size or position it further from the camera

valid cloud
#

I used a simple switch statement (using React so inside a useEffect ) to change camera max distance - but you could do anything -change the positions of models etc. to fit mobile etc.

    switch (true) {
      case state.size.width / pixelRatio < 600: //mobile
        state.camera.position.z = 8;
        setMaxDistance(9.5);
        console.log('mobile');
        break;```
green furnace
valid cloud
#

Sorry - should have expanded state is got from
import { useThree } from '@react-three/fiber';
then inside your comonent
const state = useThree();

SetMaxDistance is a useState setter function that is then used by the orbit controls for exmaple

        //left
        minAzimuthAngle={-Math.PI * 0.5}
        //right
        maxAzimuthAngle={Math.PI * -0.04}
        //top
        minPolarAngle={Math.PI / 6}
        //bottom
        maxPolarAngle={Math.PI - Math.PI / 2}
        maxDistance={maxDistance}
        enablePan={false}
      />```
#
  • not to say this is the way to go - I don't know any others, but has worked perfectly for me on a last few projects
#

to add also - the UseEffect is ran when state.size.width changes...

    switch (true) {
      case state.size.width / pixelRatio < 600: //mobile
        state.camera.position.z = 8;
        setMaxDistance(9.5);
        console.log('mobile');
        break;

      case state.size.width / pixelRatio < 1200: //laptop: 1200
        state.camera.position.z = 6;
        setMaxDistance(7);
        console.log('medium');
        break;

      case state.size.width / pixelRatio < 2000: //laptop: 1920
        state.camera.position.z = 3.5;
        setMaxDistance(6);

        console.log('large');
        break;

      default:
        state.camera.position.z = 3.5;
    }
  }, [state.size.width]); ```
#

or you could get screen size on load but found this more dynamic

green furnace
#

let me show you what i have done so far, it's getting a bit confusing

until then i'll read your messages

#
export default function Scene()
{
    const { nodes } = useGLTF( gltf_File );  
    const { camera } = useThree();


    const handleResize = () => {
        // Do something when the window is resized
        console.log('Window resized!');

        console.log("width", window.screen.width);
        console.log("height", window.screen.height);
        console.log("pixel ratio", window.devicePixelRatio);
        console.log( window.screen.width / window.devicePixelRatio );

        const width = window.screen.width;
        const height = window.screen.height;
        const pixelRatio = window.devicePixelRatio;
        const ratio = width / pixelRatio;

      
        switch (true) {
            
            case ( ratio <= 600 ): //mobile
              // state.camera.position.z = 8;
              // setMaxDistance(9.5);
              console.log('mobile');
              console.log(camera);
              camera.position.z = -8;

              break;
            case ( ratio > 600 && ratio <= 850 ):
                console.log('tablet');
                break;
            case ( ratio > 850 && ratio <= 1300 ):
                console.log('desktop');
                break;
            case (ratio > 1300 ):
                console.log('HUGE MONITOR');
                break;

            default: console.log("hehe");

              }
      };
 
    useEffect(() => {
        window.addEventListener('resize', handleResize);
       
        },[]);

    return(
        <group dispose={null}>
            <OrbitControls />

            <AmbientLightComponent />
            <CameraComponent />
            
            <BackdropComponent nodes = { nodes } /> 
            <Character_Michael_Myers_Component nodes = { nodes } />
        </group>
    )
}

useGLTF.preload( gltf_File )
#

don't focus much on screen sizes as i just put them as placeholders for now!

#

i have moved the code to useEffect to automatically adjust camera once the component mounts

#

The issue right now is the following: if i console the camera, the position does get updated. But nothing happens on screen, the camera does not change it's position on screen

I tried using useState hook but no avail

valid cloud
#

similar enough to what I have -
just what I was about to suggest - let useEffect have control over the camera updates as it will run every time there is change in canvas width + loads correct values on first load (make sure to set state.size.width as a dependency) - otherwise , and I think this is where yours in failing - your relying on another source to re render the component and thus the updates to the camera are not being met

green furnace
#
export default function Scene()
{
    const { nodes } = useGLTF( gltf_File );  
    const { camera } = useThree();

    const [ value, setValue ] = useState( false );

    useEffect(() => {
        const width = window.screen.width;
        const height = window.screen.height;
        const pixelRatio = window.devicePixelRatio;
        const ratio = width / pixelRatio;

      
        switch (true) {
            
            case ( ratio <= 600 ): //mobile
                camera.position.z = -8;
                setValue ( true );
                break;

            default: console.log("hehe");

              }
        },[]);

    return(
        <group dispose={null}>
            <OrbitControls />

            <AmbientLightComponent />
            <CameraComponent />
            
            <BackdropComponent nodes = { nodes } /> 
            <Character_Michael_Myers_Component nodes = { nodes } />
        </group>
    )
}

useGLTF.preload( gltf_File )
valid cloud
#

component code in full if it helps (minus the import/rendering of other components -)


export default function Scene() {
  const directionalLight = useRef();
  const [maxDistance, setMaxDistance] = useState(6);

  const state = useThree();

  // Responsivness - update camera zoom dependant on screen width/device pixel ratio default 4
  useEffect(() => {
    switch (true) {
      case state.size.width / pixelRatio < 600: //mobile
        state.camera.position.z = 8;
        setMaxDistance(9.5);
        console.log('mobile');
        break;

      case state.size.width / pixelRatio < 1200: //laptop: 1200
        state.camera.position.z = 6;
        setMaxDistance(7);
        console.log('medium');
        break;

      case state.size.width / pixelRatio < 2000: //laptop: 1920
        state.camera.position.z = 3.5;
        setMaxDistance(6);

        console.log('large');
        break;

      default:
        state.camera.position.z = 3.5;
    }
  }, [state.size.width]);

  // useHelper(directionalLight, THREE.DirectionalLightHelper, 1);

  return (
    <>
      <OrbitControls
        //left
        minAzimuthAngle={-Math.PI * 0.5}
        //right
        maxAzimuthAngle={Math.PI * -0.04}
        //top
        minPolarAngle={Math.PI / 6}
        //bottom
        maxPolarAngle={Math.PI - Math.PI / 2}
        maxDistance={maxDistance}
        enablePan={false}
      />
      <Environment files={'./workshop_1k.hdr'} />

      <directionalLight
        ref={directionalLight}
        intensity={2}
        castShadow
        position={[2, 2, 3]}
        shadow-camera-top={2}
        shadow-camera-right={3}
        shadow-camera-bottom={-2.2}
        shadow-camera-left={-1.5}
        shadow-camera-near={1}
        shadow-camera-far={8}
        shadow-mapSize={[1024, 1024]}
        shadow-normalBias={0.005}
      />

    
    </>
  );
} ```
green furnace
#

HEY

#

IT'S WORKING!

#

i changed to match your code

#

and it's working

#

the camera changes it's position on canvas and i can see it!

#

but there is one issue

#

it happens only when i resize the browser! not when the component initially renders

#

wait

valid cloud
#

I'd add a dependancy to your useEffect (screen width) and check what ratio is your switch statment maybe not being met ? - also what are you doing with the state (value) - your just setting it

green furnace
#

i added a variable "state.camera.position" to useEffect variable array

#

and it seems to be working

#
      const width = state.size.width;
        const pixelRatio = window.devicePixelRatio;
        const ratio = width / pixelRatio;

        console.log(state);
      
        switch (true) {
            
            case ( ratio <= 600 ): //mobile
                state.camera.position.z = 8;
                break;

            default: state.camera.position.z = 3.5;

              }
        },[state.size.width, state.camera.position]);
#

look at the last line

valid cloud
#

happy days - maybe be wary adding state.camera.position as a 2nd dependency , could cause a lot of renders

green furnace
#

it makes sense right, like looks like:

the component get's rendered first
then we are changing the camera position using useEffect() and since useEffect runs after "render of jsx" we can't see the camera move

hence if we add that "state.camera.position" it triggers the render

green furnace
valid cloud
#

yup exactly - infinite loops

happy to help - as said there may be better ways but haven't come across any other solution as yet , if you hear of any let me know

green furnace
#

Good day!

green furnace
# valid cloud yup exactly - infinite loops happy to help - as said there may be better ways...

Hey! I don't think this solution is feasible as I feel there will be a lot of boilerplate code but i'll share anyways: https://github.com/pmndrs/react-three-fiber/discussions/647#discussioncomment-59767

GitHub

What is the best way to modify the scale of a 3D animated object for different devices? I use vs instead of rem for text, which works quite well but I am not sure how to achieve this for the object...

valid cloud
# green furnace Hey! I don't think this solution is feasible as I feel there will be a lot of bo...

cheers @green furnace looks promising - must get a look - although seems a while from last update - will need to update certain aspects - but nice idea to let global css units take charge -
I wonder if something like https://www.npmjs.com/package/react-responsive could be utilised in its space, it does something similar
you could pass in all the information to a component (on screen positions etc) and conditionally position that component based on retina and screen width etc

green furnace
valid cloud
#

cheers @green furnace sorry only getting back -on holidays there sunglasses_cat
yeah, using orbit controls from drie a lot abstracted away so need extra outside basic usage , I'll defo dive deeper next project with the new info , much appreciated ! 👊

spiral breach
#

I am kind of new to r3f and even I was wondering if we could change the camera zoom based on viewport and i stumbled upon this thread. I see a quite big conversation . Would you please help me by actually leting me know which part of the chat should i refer to get this done?

#

@green furnace

green furnace
spiral breach
#

ohh i want to change according to view port size

#

currently if i look the website in mobile it cuts of the model

#

i dont want that happening

green furnace
# spiral breach i dont want that happening

essentially what you wanna change is the "aspect ratio" of the renderer and camera + adjust the camera view frustrum

you would have to decide all of the above factors on the basis of devicepixelration, screen width!

#

I'll drop in a code later, if i get time!

green furnace
spiral breach
#

ohhh

#

okayy

#

i ll try that