Skip to content

Commit f3525b1

Browse files
committed
Layout fixes; dynamic FOV based on window size
1 parent 1f9c85f commit f3525b1

File tree

1 file changed

+43
-10
lines changed

1 file changed

+43
-10
lines changed

src/components/Viewer.jsx

+43-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Canvas } from '@react-three/fiber';
22
import { XR, XROrigin, createXRStore } from '@react-three/xr';
33
import * as THREE from 'three';
44
import ImmersiveImageMesh from './ImmersiveImageMesh';
5-
import { useRef, useState } from 'react';
5+
import { useEffect, useRef, useState } from 'react';
66
import { PerspectiveCamera } from '@react-three/drei';
77

88
const store = createXRStore();
@@ -14,12 +14,37 @@ const cameraLayers = new THREE.Layers();
1414
cameraLayers.enable(0);
1515
cameraLayers.enable(1);
1616

17+
const clamp = (val, min, max) => {
18+
return Math.min(Math.max(val, min), max);
19+
}
20+
21+
const linearScale = (factor, minInput, maxInput, minOutput, maxOutput, shouldClamp = true) => {
22+
if (shouldClamp) {
23+
factor = clamp(factor, minInput, maxInput);
24+
}
25+
26+
return minOutput + (maxOutput - minOutput) *
27+
(factor - minInput) / (maxInput - minInput);
28+
}
29+
1730
export default function Viewer() {
1831
const [imageSrc, setImageSrc] = useState("");
1932
const [isInXR, setIsInXR] = useState(false);
2033
const [isDraggingOver, setIsDraggingOver] = useState(false);
2134
const cameraRef = useRef();
2235

36+
const resetFOV = () => {
37+
// Experimentally determined values
38+
cameraRef.current.fov = linearScale(window.innerWidth / window.innerHeight, 0.30, 2.5, 120, 70, true);
39+
cameraRef.current.updateProjectionMatrix();
40+
}
41+
42+
const handleResize = () => {
43+
if (!isInXR && cameraRef.current) {
44+
resetFOV();
45+
}
46+
};
47+
2348
const processBrowsedFile = (e) => {
2449
if (!(e.target.files && e.target.files.length)) return;
2550

@@ -39,8 +64,7 @@ export default function Viewer() {
3964
if (!newIsInXR && cameraRef.current) {
4065
cameraRef.current.rotation.set(...DEFAULT_CAMERA_ROTATION);
4166
cameraRef.current.position.set(...DEFAULT_CAMERA_POSITION_M);
42-
cameraRef.current.fov = DEFAULT_FOV;
43-
cameraRef.current.updateProjectionMatrix();
67+
resetFOV();
4468
}
4569

4670
setIsInXR(newIsInXR);
@@ -73,6 +97,13 @@ export default function Viewer() {
7397
setIsDraggingOver(false);
7498
};
7599

100+
useEffect(() => {
101+
window.addEventListener('resize', handleResize);
102+
return () => {
103+
window.removeEventListener('resize', handleResize);
104+
};
105+
}, []);
106+
76107
return (
77108
<div className='w-full h-full min-h-screen bg-black absolute flex items-center' onDragOver={handleDragOver} onDrop={handleDrop} onDragLeave={handleDragLeave}>
78109
<Canvas
@@ -90,14 +121,16 @@ export default function Viewer() {
90121
</XR>
91122
</Canvas>
92123

93-
<div className='fixed left-1/2 -translate-x-1/2 bottom-0 bg-white/80 flex justify-center gap-4 md:gap-12 p-4 rounded-t-md'>
94-
<div className='flex flex-col gap-1'>
95-
<p className='font-semibold'>Drop or select an Apple Spatial Photo <span className='font-mono'>(.heic)</span></p>
96-
<input className='cursor-pointer' type="file" accept=".heic" onChange={processBrowsedFile} />
97-
<p className='text-xs italic'>Your photo is processed on your device.</p>
98-
</div>
124+
<div className='fixed w-full flex justify-center bottom-0'>
125+
<div className='absolute mx-4 bottom-4 bg-white/80 flex justify-center items-stretch flex-wrap gap-y-4 gap-x-8 p-2 md:p-4 rounded-md'>
126+
<div className='flex flex-col gap-1'>
127+
<p className='font-semibold'>Drop or select an Apple Spatial Photo <span className='font-mono'>(.heic)</span></p>
128+
<input className='cursor-pointer' type="file" accept=".heic" onChange={processBrowsedFile} />
129+
<p className='text-xs italic'>Your photo is processed on your device.</p>
130+
</div>
99131

100-
<button className='bg-amber-600 outline-double outline-0 hover:outline-2 active:outline-4 focus:outline-2 outline-white text-white px-8 py-2 font-semibold text-xl rounded-md transition-all duration-[25ms]' onClick={onXRButtonClicked}>{isInXR ? 'Exit' : 'Enter'} VR</button>
132+
<button className='bg-amber-600 outline-double outline-0 hover:outline-2 active:outline-4 focus:outline-2 outline-white text-white px-8 py-2 font-semibold text-xl rounded-md transition-all duration-[25ms]' onClick={onXRButtonClicked}>{isInXR ? 'Exit' : 'Enter'} VR</button>
133+
</div>
101134
</div>
102135

103136
<div className={`absolute inset-0 rounded-md border-dashed border-2 border-white bg-neutral-800/90 text-amber-500 font-semibold text-xl md:text-2xl flex-col gap-2 justify-center items-center ${isDraggingOver ? "flex" : "hidden"} pointer-events-none`}>

0 commit comments

Comments
 (0)