Skip to content

Commit

Permalink
FIx lines and globe reset - WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Bluesmile82 committed Oct 15, 2024
1 parent 33b1a76 commit 2a0891e
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 29 deletions.
34 changes: 25 additions & 9 deletions src/components/globe-map/camera.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { useRef } from "react";
import { convertLatLonToGlobalPosition } from "@/lib/globe-utils";
import type { MarkerType } from "./marker";

export const Camera = ({ marker }: {
export const Camera = ({ marker, disabled = false }: {
marker: MarkerType | undefined
disabled: boolean
}) => {
const cameraControlsRef = useRef<CameraControls>(null!);
const [resetControls, setResetControls] = useState<boolean>(true);
Expand All @@ -18,19 +19,34 @@ export const Camera = ({ marker }: {
}, [marker]);

useEffect(() => {
if (marker !== undefined) {
const [x, y, z] = convertLatLonToGlobalPosition(marker.lat, marker.lng, 2);
cameraControlsRef.current.disconnect();
cameraControlsRef.current.setPosition(x, y, z, true);
if (cameraControlsRef.current) {
if (marker !== undefined) {
const [x, y, z] = convertLatLonToGlobalPosition(marker.lat, marker.lng, 2);
cameraControlsRef.current.disconnect();
cameraControlsRef.current.setPosition(x, y, z, true);
}

if (resetControls) {
cameraControlsRef.current.connect(canvasElement);
cameraControlsRef.current.setPosition(0, 1, 4.9, true);
cameraControlsRef.current.setTarget(0, 0, 0, true);
setResetControls(false);
}
}
}, [marker, resetControls, canvasElement]);

if (resetControls) {
useEffect(() => {
if (disabled && cameraControlsRef.current) {
cameraControlsRef.current.connect(canvasElement);
cameraControlsRef.current.setPosition(0, 1, 4.9, true);
cameraControlsRef.current.setPosition(0, 1, 2, true);
cameraControlsRef.current.setTarget(0, 0, 0, true);
setResetControls(false);
setResetControls(true);
}
}, [marker, resetControls, canvasElement]);
}, [disabled, cameraControlsRef.current]);

if (disabled) {
return null;
}

return (
<CameraControls
Expand Down
18 changes: 14 additions & 4 deletions src/components/globe-map/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { useState, useRef, useEffect } from 'react';
import { useState, useRef, useEffect, CSSProperties } from 'react';
import { Canvas } from '@react-three/fiber'
import { Camera } from './camera';
import { Globe } from './globe';
Expand All @@ -16,10 +16,11 @@ const markers: MarkerType[] = [
{ id: "Cape Town", lat: -33.9249, lng: 18.4241 },
];

export default function GlobeMap({ videoMaterial, className, hasMarkers = false, rotate = false }:
export default function GlobeMap({ videoMaterial, className, style, hasMarkers = false, rotate = false }:
{
videoMaterial: string,
className: string,
style?: CSSProperties,
hasMarkers?: boolean,
rotate?: boolean,
}
Expand Down Expand Up @@ -50,17 +51,26 @@ export default function GlobeMap({ videoMaterial, className, hasMarkers = false,
<>
<div
className={className}
style={style}
>
<Canvas
camera={{ fov: 35 }}
ref={canvasRef}
resize={{ scroll: false, debounce: { scroll: 0, resize: 0 } }}
>
{hasMarkers && <Camera marker={marker} />}
<Camera marker={marker} disabled={!hasMarkers} />
<ambientLight intensity={7} />
<Globe videoMaterial={videoMaterial} rotate={rotate} />
{hasMarkers && markers.map((marker, index) => (
<Marker key={index} index={markers.indexOf(marker)} id={marker.id} isSelected={selectedMarker === markers.indexOf(marker)} setSelectedMarker={setSelectedMarker} lat={marker.lat} lng={marker.lng}>
<Marker
key={index}
index={markers.indexOf(marker)}
id={marker.id}
isSelected={selectedMarker === markers.indexOf(marker)}
setSelectedMarker={setSelectedMarker}
lat={marker.lat}
lng={marker.lng}
>
Click to explore the phenomenon
</Marker>
))}
Expand Down
79 changes: 63 additions & 16 deletions src/components/section-2/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { useRef, useEffect, useState, useCallback } from 'react';
import { useRef, useEffect, useState, useCallback, useMemo } from 'react';
import Lines from "@/components/lines";
import GlobeMap from "@/components/globe-map";
import { Resizable } from 're-resizable';
Expand All @@ -24,6 +24,20 @@ const ResizeButton = () => (
</>
);

const useScreenWidthWithResize = () => {
const [width, setWidth] = useState(window.innerWidth);
const handleResize = useCallback(() => {
setWidth(window.innerWidth);
}, []);

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

return width;
}

export default function Section2() {
const scrollSectionRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
Expand All @@ -32,6 +46,7 @@ export default function Section2() {
const [globePhase, setGlobePhase] = useState(0);
const [resizableWidth, setResizableWidth] = useState(400);

const screenWidth = useScreenWidthWithResize();
const scrollToSection = (section: string) => {
const element = document.getElementById(section);
if (element) {
Expand All @@ -51,35 +66,51 @@ export default function Section2() {
useMotionValueEvent(scrollYProgress, "change", (latest) => {
if (latest >= 0 && latest < 0.1 && globePhase !== 0) {
setGlobePhase(0);
setResizableWidth(400);
setResizableWidth(screenWidth / 2);
}

if (latest >= 0.1 && latest < 0.66 && globePhase !== 1) {
setGlobePhase(1);
setResizableWidth(800);
setResizableWidth(screenWidth);
}

if (latest >= 0.66 && globePhase < 2) {
setGlobePhase(2);
}
});

const descriptionRef = useRef<HTMLDivElement>(null);

const descriptionLeft = useMemo(() => {
if (descriptionRef.current) {
return descriptionRef.current.getBoundingClientRect().left;
}
return 0;
}, [descriptionRef.current, screenWidth]);


const titleX = useTransform(scrollYProgress, [0.2, 0.33], [0, -200]);
const titleY = useTransform(scrollYProgress, [0.2, 0.33], [0, -200]);
const descriptionY = useTransform(scrollYProgress, [0.2, 0.33, 0.6, 0.7], [500, 0, 0, -1000]);
const titleY = useTransform(scrollYProgress, [0.2, 0.33], [0, -204]);
const descriptionY = useTransform(scrollYProgress, [0.2, 0.33, 0.6, 0.7], [500, -4, -4, -1000]);

const opacityLine = useTransform(scrollYProgress, [0.2, 0.33, 0.6, 0.7], [0, 1, 1, 0]);
const lineY = useTransform(scrollYProgress, [0.2, 0.33, 0.6, 0.7], [500, -40, -40, -1000]);
const lineX = useTransform(scrollYProgress, [0.2, 0.33, 0.6, 0.7], [resizableWidth, descriptionLeft, descriptionLeft, descriptionLeft]);


return (
<section className="relative bg-green-700">
<div className='relative pointer-events-none'>
<Lines verticalClassName="left-8" sectionName="section-1" columns={[100]} rows={[100]} colorClass="bg-blue-900/20" />
<Lines verticalClassName="left-8" sectionName="section-1" columns={[100]} rows={[100]} colorClass="bg-blue-900/10" />
</div>

<div className="relative h-[300vh] snap-y snap-mandatory" ref={scrollSectionRef}>
<div className='h-screen flex justify-center sticky inset-0 snap-start snap-always' id="globe-phase-1">
<div className='relative'>
{/* High globe */}
<GlobeMap
className='w-[800px] h-full'
className='h-full'
style={{ width: screenWidth }}
videoMaterial="videos/wind_speed_global_100km.mp4"
/>
<div className="absolute inset-0 w-full z-30">
Expand All @@ -94,7 +125,8 @@ export default function Section2() {
enable={{
right: globePhase === 0,
}}
maxWidth="800px"

maxWidth={screenWidth}
minWidth="1"
handleComponent={{
right: <ResizeButton />,
Expand All @@ -103,31 +135,46 @@ export default function Section2() {
<div className='relative w-full h-full overflow-hidden'>
{/* Low globe */}
<GlobeMap
className='transform h-full w-[800px]'
className='transform h-full'
style={{ width: screenWidth }}
videoMaterial="videos/wind_speed_global_10km.mp4"
hasMarkers={globePhase > 1} rotate={globePhase === 1}
hasMarkers={globePhase > 1}
rotate={globePhase === 1}
/>
</div>
</Resizable>
</div>
</div>
{/* Vertical description line */}
<motion.div
className='absolute left-0 top-0 w-px h-full bg-light-green/30 z-30'
style={{ x: lineX, opacity: opacityLine }}
/>
<motion.div className='absolute w-full h-full z-30 flex flex-col items-center top-[40%] transition-opacity duration-500 pointer-events-none'
style={{
opacity: globePhase > 1 ? 0 : 1,
}}
>
{/* Horizontal description line */}
<motion.div
className='absolute left-0 top-0 h-px w-full bg-light-green/30'
style={{ y: lineY, opacity: opacityLine }}
/>
<div>
<motion.div style={{
x: titleX,
y: titleY,
}}>
<div className="text-center text-light-green text-lg uppercase tracking-tight">UNLOCKING CLIMATE POTENTIAL</div>
<div className="text-center text-light-green text-4xl max-w-[720px]">
High-quality information from global to local scale
<div className='translate-y-3'>
<div className="text-center text-light-green text-lg uppercase tracking-tight">UNLOCKING CLIMATE POTENTIAL</div>
<div className="text-center text-light-green text-4xl max-w-[720px]">
High-quality information from global to local scale
</div>
</div>
<motion.div
className="text-light-green leading-relaxed w-[500px] flex flex-col gap-6"
style={{ y: descriptionY, x: 700 }}
style={{ y: descriptionY, x: 710 }}
ref={descriptionRef}
>
<p>
At the resolutions that global climate models use today, a number of small-scale processes that are important for the simulation of extreme events and the evolution of the climate system, are not directly represented. Increasing the model resolution (i.e. reducing the size of grid cells used in climate models both horizontally and vertically) allows researchers to represent these processes more directly.
Expand All @@ -146,7 +193,7 @@ export default function Section2() {
{/* Empty divs for the snap scroll */}
<div className='h-[100vh] snap-start snap-always' id="globe-phase-2"></div>
<div className='h-[100vh] snap-start snap-always' id="globe-phase-3"></div>
</div>
</section>
</div >
</section >
);
};

0 comments on commit 2a0891e

Please sign in to comment.