diff --git a/.github/workflows/sync.yaml b/.github/workflows/sync.yaml index 61ea0cf..93c24a7 100644 --- a/.github/workflows/sync.yaml +++ b/.github/workflows/sync.yaml @@ -37,6 +37,11 @@ jobs: cd ./classes/week-8/animated-highlights-r3f npm install npm run build + - name: Build Week 10 - Data, Controls, Interactions + run: | # Week 8 - Animated Highlights + cd ./classes/week-10/data-controls-interactions-r3f + npm install + npm run build # Minimal test of the GitHub Action # (only runs on pushes to the `main` branch) # Glitch Project: https://glitch.com/edit/#!/github-action diff --git a/classes/week-10/data-controls-interactions-r3f/.gitignore b/classes/week-10/data-controls-interactions-r3f/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/classes/week-10/data-controls-interactions-r3f/README.md b/classes/week-10/data-controls-interactions-r3f/README.md new file mode 100644 index 0000000..1868345 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/README.md @@ -0,0 +1 @@ +# ITP Visual Journalism Fall 2023 - Interactions & Controls & Data diff --git a/classes/week-10/data-controls-interactions-r3f/index.html b/classes/week-10/data-controls-interactions-r3f/index.html new file mode 100644 index 0000000..f290727 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/index.html @@ -0,0 +1,12 @@ + + + + + + ITP Visual Journalism Fall 2023 - Week 10 + + +
+ + + diff --git a/classes/week-10/data-controls-interactions-r3f/package.json b/classes/week-10/data-controls-interactions-r3f/package.json new file mode 100644 index 0000000..6f1c762 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/package.json @@ -0,0 +1,29 @@ +{ + "name": "data-controls-interactions-r3f", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@react-three/drei": "^9.80.4", + "@react-three/fiber": "^8.13.7", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "three": "^0.155.0" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@types/three": "^0.155.0", + "@vitejs/plugin-react": "^4.0.3", + "vite": "^4.3.8" + }, + "engines": { + "node": "16.x" + } +} diff --git a/classes/week-10/data-controls-interactions-r3f/public/earth.jpg b/classes/week-10/data-controls-interactions-r3f/public/earth.jpg new file mode 100644 index 0000000..73ef57f Binary files /dev/null and b/classes/week-10/data-controls-interactions-r3f/public/earth.jpg differ diff --git a/classes/week-10/data-controls-interactions-r3f/src/App.css b/classes/week-10/data-controls-interactions-r3f/src/App.css new file mode 100644 index 0000000..8523551 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/src/App.css @@ -0,0 +1,18 @@ +body { + /* Reseting the default here */ + margin: 0; +} + +#article_wrapper { + position: relative; + background-color: grey; + height: 100vh; /* Length of the interactive scroll article */ +} + +#canvas_wrapper { + position: -webkit-sticky; /* Safari */ + position: sticky; + top: 0; + width: 100vw; + height: 100vh; +} diff --git a/classes/week-10/data-controls-interactions-r3f/src/App.jsx b/classes/week-10/data-controls-interactions-r3f/src/App.jsx new file mode 100644 index 0000000..96aaad3 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/src/App.jsx @@ -0,0 +1,15 @@ +import Scene from "./Scene"; + +// We add a CSS file here so we can style components +import "./App.css"; + +function App() { + return ( +
+ {/* 3D scene container */} + +
+ ); +} + +export default App; diff --git a/classes/week-10/data-controls-interactions-r3f/src/Earth.jsx b/classes/week-10/data-controls-interactions-r3f/src/Earth.jsx new file mode 100644 index 0000000..6e588f8 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/src/Earth.jsx @@ -0,0 +1,13 @@ +import { useTexture } from "@react-three/drei"; + +function Earth() { + const earthTexture = useTexture("/earth.jpg"); + return ( + + + + + ); +} + +export default Earth; diff --git a/classes/week-10/data-controls-interactions-r3f/src/LocationMarker.jsx b/classes/week-10/data-controls-interactions-r3f/src/LocationMarker.jsx new file mode 100644 index 0000000..a1594c6 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/src/LocationMarker.jsx @@ -0,0 +1,43 @@ +import { useState } from "react"; +import { Html } from "@react-three/drei"; + +function LocationMarker({ position, name }) { + const [isExpanded, setIsExpanded] = useState(false); + return ( + <> + { + if (!isExpanded) setIsExpanded(true); + }} + onPointerLeave={() => { + if (isExpanded) setIsExpanded(false); + }} + position={position} + > + {isExpanded && ( + +
+

{name}

+

+ {name} - here we can add more details dynamically from the data + - or by stitching other data! +

+
+ + )} + + +
+ + ); +} + +export default LocationMarker; diff --git a/classes/week-10/data-controls-interactions-r3f/src/Scene.jsx b/classes/week-10/data-controls-interactions-r3f/src/Scene.jsx new file mode 100644 index 0000000..dbbf125 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/src/Scene.jsx @@ -0,0 +1,90 @@ +import { useEffect, useState } from "react"; +import { Vector3 } from "three"; +import { Canvas } from "@react-three/fiber"; +import { + Environment, + OrbitControls, + PerspectiveCamera, +} from "@react-three/drei"; + +import LocationMarker from "./LocationMarker"; +import Earth from "./Earth"; + +// This function converts geographic coordinates (latitude and longitude) into a 3D vector. +function latLongToVector3(lat, lon, radius) { + // Convert latitude and longitude from degrees to radians + const phi = (lat * Math.PI) / 180; + const theta = ((lon - 180) * Math.PI) / 180; + + // Calculate the x, y, and z coordinates using the spherical to Cartesian coordinates conversion formulas + const x = -(radius * Math.cos(phi) * Math.cos(theta)); + const y = radius * Math.sin(phi); + const z = radius * Math.cos(phi) * Math.sin(theta); + + return new Vector3(x, y, z); +} + +function Scene() { + // We use state to store our data points + const [dataPoints, setDataPoints] = useState([]); + + // We fetch our data and do any pre-processing needed to organize it + useEffect(() => { + fetch( + "https://raw.githubusercontent.com/eesur/country-codes-lat-long/master/country-codes-lat-long-alpha3.json" + ).then((res) => { + res.json().then((data) => { + const { ref_country_codes } = data; + + const filteredData = ref_country_codes.filter( + (item) => item.latitude && item.longitude + ); + const mappedData = filteredData.map( + ({ longitude, latitude, country }) => { + return { + position: latLongToVector3(latitude, longitude, 1), + name: country, + }; + } + ); + setDataPoints(mappedData); + }); + }); + }, []); + + return ( +
+ + + + + + + + {/* Lights 💡 */} + + + + + + {/* Data points turned into geometry with it's own interaction 📌 */} + {dataPoints.map((item, index) => ( + + ))} + +
+ ); +} + +export default Scene; diff --git a/classes/week-10/data-controls-interactions-r3f/src/main.jsx b/classes/week-10/data-controls-interactions-r3f/src/main.jsx new file mode 100644 index 0000000..35c73b7 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/src/main.jsx @@ -0,0 +1,5 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.jsx"; + +ReactDOM.createRoot(document.getElementById("root")).render(); diff --git a/classes/week-10/data-controls-interactions-r3f/vite.config.js b/classes/week-10/data-controls-interactions-r3f/vite.config.js new file mode 100644 index 0000000..e46c617 --- /dev/null +++ b/classes/week-10/data-controls-interactions-r3f/vite.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// https://vitejs.dev/config/ +export default defineConfig({ + server: { + port: 3000, + }, + plugins: [react()], +});