#How to make my react three fiber website responsive
56 messages · Page 1 of 1 (latest)
maybe do a if statement screen lower than 765px to decrease the size or position it further from the camera
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;```
let me try this: what is state here? i am assuming it's the useThree hook
i am new to r3f so sorry if it's a newbie question
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
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
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
So, let me show you a cleaned conscise code, i'll explain what i have changed
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 )
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}
/>
</>
);
} ```
I cleaned up the code for you!
So, inside useEffect, even if i set the camera ~ it still doesn't show any change on screen
so i tried using a state variable with usestate hook and still the camera doesn't show any change
But in console log the values do change
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
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
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
happy days - maybe be wary adding state.camera.position as a 2nd dependency , could cause a lot of renders
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
all thanks to you man! it was very fun to solve this issue with you!
I apprecite the help 🫡
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
yes! i will def let ya know!
:)
Good day!
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
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
hey! forgot to tell you ~ i found out that whenever you update / change any of the default camera props you have to do a update on cameras matrix position
else the changes won't reflect. i can't see you updating it in your code
this is what i have gathered! if you think i am wrong please let me know
you can read more here in this thread: https://discord.com/channels/685241246557667386/1139452132886581248
cheers @green furnace sorry only getting back -on holidays there 
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 ! 👊
all good my guy!
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
actually none of it
i'll explain what you have to do!
In code you can change / update camera positions as you would like and then call "camera.updateProjectionMatrix()"
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
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!
so something like this plus a call to "camera.updateProjectionMatrix()" should do the job