Skip to content

Commit

Permalink
Fix globe interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Bluesmile82 committed Oct 17, 2024
1 parent f982925 commit 0cb1e17
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 36 deletions.
57 changes: 30 additions & 27 deletions src/components/globe-map/controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,18 @@ import { convertLatLonToGlobalPosition } from "@/lib/globe-utils";
import type { MarkerType } from "./marker";
import { Group } from "three";

export const Controls = ({ marker, disabled = false, groupRef, resetSelectedMarker }: {
export const Controls = ({ marker, active = false, enabled = false, groupRef, resetSelectedMarker, setEnabled }: {
marker: MarkerType | undefined
disabled: boolean
// Active is used to determine if the globe controls are in a phase that could be enabled even if is temporarily disabled
active: boolean
// Enabled is used to determine if the globe controls are currently enabled
enabled: boolean
setEnabled: (enabled: boolean) => void
groupRef: React.MutableRefObject<Group>
resetSelectedMarker: () => void
resetSelectedMarker: () => void,
}) => {
const cameraControlsRef = useRef<CameraControls>(null!);
const [resetControls, setResetControls] = useState<boolean>(true);
const canvasElement = useMemo(() => document.getElementsByTagName('canvas')[0], [document]);

useEffect(() => {
if (marker === undefined) {
setResetControls(true);
}
}, [marker]);
const [resettingPosition, setResettingPosition] = useState(false);

const resetPosition = () => {
groupRef.current.rotation.y = 0;
Expand All @@ -28,35 +25,41 @@ export const Controls = ({ marker, disabled = false, groupRef, resetSelectedMark
};

useEffect(() => {
if (cameraControlsRef.current) {
if (marker !== undefined) {
const [x, y, z] = convertLatLonToGlobalPosition(marker.lat, marker.lng, 2);
cameraControlsRef.current.disconnect();
groupRef.current.rotation.y = 0;
cameraControlsRef.current.setPosition(x, y, z, true);
}
if (active && marker !== undefined) {
const [x, y, z] = convertLatLonToGlobalPosition(marker.lat, marker.lng, 2);
groupRef.current.rotation.y = 0;
cameraControlsRef.current.setPosition(x, y, z, true).then(() => {
setEnabled(false);
});
}

if (resetControls) {
cameraControlsRef.current.connect(canvasElement);
if (enabled) {
if (marker === undefined) {
resetSelectedMarker();
resetPosition();
setResetControls(false);
}
}
}, [marker, resetControls, canvasElement]);

useEffect(() => {
if (disabled) {
resetSelectedMarker();
resetPosition();
if (!active && marker !== undefined && !resettingPosition) {
setResettingPosition(true);
}
}, [disabled, cameraControlsRef.current]);

if (resettingPosition) {
groupRef.current.rotation.y = 0;
cameraControlsRef.current.setTarget(0, 0, 0, true);
cameraControlsRef.current.setPosition(0, 1, 4.9, true).then(() => {
setResettingPosition(false);
setEnabled(false);
});
}
}, [enabled, marker, active, resettingPosition]);
return (
<CameraControls
ref={cameraControlsRef}
makeDefault
dollySpeed={0}
polarRotateSpeed={0}
azimuthRotateSpeed={enabled || resettingPosition ? 1 : 0}
/>
);
};
6 changes: 4 additions & 2 deletions src/components/globe-map/globe-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { useFrame } from '@react-three/fiber';
import type { markers as MarkerType } from './data';
import { Group } from 'three';

const GlobeGroup = ({ hasMarkers, markers, selectedMarker, setSelectedMarker, rotate, videoMaterial, groupRef }:
const GlobeGroup = ({ hasMarkers, markers, selectedMarker, setEnabled, setSelectedMarker, rotate, videoMaterial, groupRef }:
{
hasMarkers: boolean,
markers: typeof MarkerType,
selectedMarker: number | null,
setEnabled: (enabled: boolean) => void,
setSelectedMarker: (index: number | null) => void,
rotate: boolean,
videoMaterial: string,
Expand All @@ -35,8 +36,9 @@ const GlobeGroup = ({ hasMarkers, markers, selectedMarker, setSelectedMarker, ro
setSelectedMarker={setSelectedMarker}
lat={marker.lat}
lng={marker.lng}
setControlsEnabled={setEnabled}
>
Click to explore the phenomenon
{index === 0 && 'Click to explore the phenomenon'}
</Marker>
))
}
Expand Down
15 changes: 14 additions & 1 deletion src/components/globe-map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default function GlobeMap({ videoMaterial, className, style, hasMarkers =
const groupRef = useRef<Group>(null!);
const marker = selectedMarker !== null ? markers[selectedMarker] : undefined;
const canvasRef = useRef<HTMLCanvasElement>(null);
const [enabled, setEnabled] = useState(false);

useEffect(() => {
const handleWheel = (event: WheelEvent) => {
Expand All @@ -43,6 +44,17 @@ export default function GlobeMap({ videoMaterial, className, style, hasMarkers =
setSelectedMarker(null);
}, []);


useEffect(() => {
if (hasMarkers && !enabled) {
setEnabled(true);
}

if (!hasMarkers && enabled) {
setEnabled(false);
}
}, [hasMarkers]);

return (
<>
<div
Expand All @@ -54,11 +66,12 @@ export default function GlobeMap({ videoMaterial, className, style, hasMarkers =
ref={canvasRef}
resize={{ scroll: false, debounce: { scroll: 0, resize: 0 } }}
>
<Controls marker={marker} disabled={!hasMarkers} groupRef={groupRef} resetSelectedMarker={resetSelectedMarker} />
<Controls marker={marker} active={hasMarkers} enabled={enabled} setEnabled={setEnabled} groupRef={groupRef} resetSelectedMarker={resetSelectedMarker} />
<ambientLight intensity={7} />
<GlobeGroup
groupRef={groupRef}
hasMarkers={hasMarkers} markers={markers} selectedMarker={selectedMarker} setSelectedMarker={setSelectedMarker} rotate={rotate}
setEnabled={setEnabled}
videoMaterial={videoMaterial}
/>
</Canvas>
Expand Down
18 changes: 12 additions & 6 deletions src/components/globe-map/marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import Close from '@/svgs/close.svg';
import { popupContent } from './data';
import { motion } from 'framer-motion';

const Popup = ({ setSelectedMarker, index }: {
setSelectedMarker:
(index: number | null) => void
const Popup = ({ closePopup, setSelectedMarker, index }: {
closePopup: () => void
setSelectedMarker: (index: number) => void
index: number,
}) => {
const { title, subtitle, description, video, legend } = popupContent[index];
Expand Down Expand Up @@ -53,7 +53,7 @@ const Popup = ({ setSelectedMarker, index }: {
</div>
</div>
</div>
<button onClick={() => setSelectedMarker(null)} className="absolute -top-4 p-[9px] bg-light-green rounded-full items-center gap-[7px] flex">
<button onClick={closePopup} className="absolute -top-4 p-[9px] bg-light-green rounded-full items-center gap-[7px] flex">
<Close
width={14}
height={14}
Expand All @@ -63,11 +63,12 @@ const Popup = ({ setSelectedMarker, index }: {
);
}

function Marker({ id, index, lat, lng, children, setSelectedMarker, isSelected, ...props }: {
function Marker({ id, index, lat, lng, children, setSelectedMarker, setControlsEnabled, isSelected, ...props }: {
id: string
index: number
lat: number
lng: number
setControlsEnabled: (enabled: boolean) => void,
setSelectedMarker: (index: number | null) => void,
isSelected: boolean,
children: React.ReactNode
Expand All @@ -85,6 +86,11 @@ function Marker({ id, index, lat, lng, children, setSelectedMarker, isSelected,
if (range !== isInRange) setInRange(range)
})

const closePopup = () => {
setControlsEnabled(true);
setSelectedMarker(null);
};

return (
<group ref={ref} >
<Html
Expand All @@ -96,7 +102,7 @@ function Marker({ id, index, lat, lng, children, setSelectedMarker, isSelected,
// We just interpolate the visible state into css opacity and transforms
style={{ transition: 'all 0.1s', opacity: isVisible ? 1 : 0, transform: `scale(${isVisible ? 1 : 0.1})` }}
{...props}>
{isSelected ? <Popup setSelectedMarker={setSelectedMarker} index={index} /> :
{isSelected ? <Popup closePopup={closePopup} setSelectedMarker={setSelectedMarker} index={index} /> :
<>
<button
type="button"
Expand Down

0 comments on commit 0cb1e17

Please sign in to comment.