mirror of
https://github.com/noahpaige/portfolio-website.git
synced 2026-06-08 16:18:01 -06:00
switch to JSX, add blobs
This commit is contained in:
+1
-1
@@ -8,6 +8,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Generated
+406
-351
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -5,10 +5,12 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-spring/types": "^9.7.1",
|
||||
"@react-three/drei": "^9.57.2",
|
||||
"@react-three/fiber": "^8.12.0",
|
||||
"@types/three": "^0.149.0",
|
||||
"daisyui": "^2.41.0",
|
||||
@@ -19,13 +21,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@types/react": "^18.0.24",
|
||||
"@types/react-dom": "^18.0.8",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"postcss": "^8.4.19",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"typescript": "^4.6.4",
|
||||
"vite": "^3.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import { useState } from "react";
|
||||
import "./App.css";
|
||||
import Home from "./Pages/Home";
|
||||
|
||||
function App() {
|
||||
export default function App() {
|
||||
return <Home />;
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,12 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import "../App.css";
|
||||
|
||||
function About() {
|
||||
export default function About() {
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<h1>About</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default About;
|
||||
@@ -0,0 +1,138 @@
|
||||
import * as THREE from "three";
|
||||
import React, { Suspense, useEffect, useState, useRef } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import {
|
||||
PerspectiveCamera,
|
||||
Environment,
|
||||
MeshDistortMaterial,
|
||||
ContactShadows,
|
||||
RenderTexture,
|
||||
Text,
|
||||
} from "@react-three/drei";
|
||||
import { useSpring } from "@react-spring/core";
|
||||
import { a } from "@react-spring/three";
|
||||
|
||||
// React-spring animates native elements, in this case <mesh/> etc,
|
||||
// but it can also handle 3rd–party objs, just wrap them in "a".
|
||||
const AnimatedMaterial = a(MeshDistortMaterial);
|
||||
|
||||
export default function Blurb() {
|
||||
const sphere = useRef();
|
||||
const light = useRef();
|
||||
const [mode, setMode] = useState(false);
|
||||
const [down, setDown] = useState(false);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const text1Ref = useRef();
|
||||
const text2Ref = useRef();
|
||||
const lerp = (start, end, amt) => {
|
||||
return (1 - amt) * start + amt * end;
|
||||
};
|
||||
|
||||
// Make the bubble float and follow the mouse
|
||||
// This is frame-based animation, useFrame subscribes the component to the render-loop
|
||||
useFrame((state) => {
|
||||
light.current.position.x = state.mouse.x * 20;
|
||||
light.current.position.y = state.mouse.y * 20;
|
||||
if (sphere.current) {
|
||||
sphere.current.position.x = THREE.MathUtils.lerp(
|
||||
sphere.current.position.x,
|
||||
hovered ? state.mouse.x / 2 : 0,
|
||||
0.2
|
||||
);
|
||||
sphere.current.position.y = THREE.MathUtils.lerp(
|
||||
sphere.current.position.y,
|
||||
Math.sin(state.clock.elapsedTime / 1.5) / 6 +
|
||||
(hovered ? state.mouse.y / 2 : 0),
|
||||
0.2
|
||||
);
|
||||
}
|
||||
const dist = 3;
|
||||
|
||||
const t1 = Math.cos(state.clock.elapsedTime);
|
||||
const p = t1 < 0 ? -1 : 1;
|
||||
const prev1 = text1Ref.current.position.x;
|
||||
const t2 = lerp(prev1 - dist, p * dist, 0.2);
|
||||
|
||||
text1Ref.current.position.x = dist + t2;
|
||||
text2Ref.current.position.x = t2 - dist;
|
||||
});
|
||||
|
||||
// Springs for color and overall looks, this is state-driven animation
|
||||
// React-spring is physics based and turns static props into animated values
|
||||
const [{ wobble, coat, color, ambient, env }] = useSpring(
|
||||
{
|
||||
wobble: down ? 1.2 : hovered ? 1.05 : 1,
|
||||
coat: mode && !hovered ? 0.04 : 1,
|
||||
ambient: mode && !hovered ? 1.5 : 0.5,
|
||||
env: mode && !hovered ? 0.4 : 1,
|
||||
color: hovered ? "#E8B059" : mode ? "#202020" : "white",
|
||||
config: (n) =>
|
||||
n === "wobble" && hovered && { mass: 2, tension: 1000, friction: 10 },
|
||||
},
|
||||
[mode, hovered, down]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PerspectiveCamera makeDefault position={[0, 0, 4]} fov={75}>
|
||||
<a.ambientLight intensity={ambient} />
|
||||
<a.pointLight
|
||||
ref={light}
|
||||
position-z={-15}
|
||||
intensity={env}
|
||||
color="#F8C069"
|
||||
/>
|
||||
</PerspectiveCamera>
|
||||
<Suspense fallback={null}>
|
||||
<a.mesh
|
||||
ref={sphere}
|
||||
scale={wobble}
|
||||
onPointerOver={() => setHovered(true)}
|
||||
onPointerOut={() => setHovered(false)}
|
||||
onPointerDown={() => setDown(true)}
|
||||
onPointerUp={() => {
|
||||
setDown(false);
|
||||
// Toggle mode between dark and bright
|
||||
setMode(!mode);
|
||||
}}
|
||||
>
|
||||
<sphereBufferGeometry args={[1, 32, 32]} />
|
||||
<AnimatedMaterial
|
||||
envMapIntensity={env}
|
||||
clearcoat={coat}
|
||||
clearcoatRoughness={0}
|
||||
metalness={0.1}
|
||||
>
|
||||
<RenderTexture width={1080} attach="map" anisotropy={32}>
|
||||
<PerspectiveCamera
|
||||
makeDefault
|
||||
manual
|
||||
aspect={1 / 1}
|
||||
position={[0, 0, 5]}
|
||||
/>
|
||||
<color attach="background" args={["orange"]} />
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[10, 10, 5]} />
|
||||
<Text ref={text1Ref} fontSize={0.3} color="#555">
|
||||
{" "}Web{"\n"}Developer
|
||||
</Text>
|
||||
<Text ref={text2Ref} fontSize={0.3} color="#555">
|
||||
{" "}Game{"\n"}Developer
|
||||
</Text>
|
||||
</RenderTexture>
|
||||
</AnimatedMaterial>
|
||||
</a.mesh>
|
||||
<Environment preset="warehouse" />
|
||||
<ContactShadows
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
position={[0, -1.6, 0]}
|
||||
opacity={mode ? 0.8 : 0.4}
|
||||
width={15}
|
||||
height={15}
|
||||
blur={2.5}
|
||||
far={1.6}
|
||||
/>
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useRef, useState } from 'react'
|
||||
import { Canvas, useFrame } from '@react-three/fiber'
|
||||
import { RenderTexture, PerspectiveCamera, Text, ContactShadows } from '@react-three/drei'
|
||||
|
||||
export default function BlurbWithText() {
|
||||
return (
|
||||
<>
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[10, 10, 5]} />
|
||||
<Blurb />
|
||||
<ContactShadows frames={1} position={[0, -1, 0]} blur={4} opacity={0.5} />
|
||||
<ContactShadows frames={1} position={[0, -1, 0]} blur={3} opacity={0.4} color="orange" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Blurb() {
|
||||
const text1Ref = useRef()
|
||||
const text2Ref = useRef()
|
||||
const lerp = (start, end, amt) => {
|
||||
return (1 - amt) * start + amt * end
|
||||
}
|
||||
useFrame((state) => {
|
||||
const dist = 3
|
||||
|
||||
const t1 = Math.cos(state.clock.elapsedTime)
|
||||
const p = t1 < 0 ? -1 : 1
|
||||
const prev1 = text1Ref.current.position.x
|
||||
const t2 = lerp(prev1 - dist, p * dist, 0.2)
|
||||
|
||||
text1Ref.current.position.x = dist + t2
|
||||
text2Ref.current.position.x = t2 - dist
|
||||
})
|
||||
return (
|
||||
<mesh position={[0,0,1]}>
|
||||
<sphereGeometry />
|
||||
<meshStandardMaterial>
|
||||
<RenderTexture width={1080} attach="map" anisotropy={32}>
|
||||
<PerspectiveCamera makeDefault manual aspect={1 / 1} position={[0, 0, 5]} />
|
||||
<color attach="background" args={['orange']} />
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[10, 10, 5]} />
|
||||
<Text ref={text1Ref} fontSize={0.3} color="#555">
|
||||
{' '}Web{'\n'}Developer
|
||||
</Text>
|
||||
<Text ref={text2Ref} fontSize={0.3} color="#555">
|
||||
{' '}Game{'\n'}Developer
|
||||
</Text>
|
||||
</RenderTexture>
|
||||
</meshStandardMaterial>
|
||||
</mesh>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
|
||||
function MyFooter() {
|
||||
export default function MyFooter() {
|
||||
return (
|
||||
<footer className="footer p-10 bg-blue-500 text-neutral-content snap-center">
|
||||
<a className="link link-hover">GITHUB</a>
|
||||
@@ -11,4 +11,3 @@ function MyFooter() {
|
||||
);
|
||||
}
|
||||
|
||||
export default MyFooter;
|
||||
@@ -0,0 +1,74 @@
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { useRef, useState } from "react";
|
||||
import { useSpring, animated, config } from "react-spring/three";
|
||||
import { MathUtils } from "three";
|
||||
import { a } from '@react-spring/three'
|
||||
|
||||
|
||||
import "../App.css";
|
||||
|
||||
export default function NoiseBlob({ position }) {
|
||||
console.log(position);
|
||||
// This reference gives us direct access to the THREE.Mesh object
|
||||
const sphere = useRef();
|
||||
// Hold state for hovered and clicked events
|
||||
const [hovered, hover] = useState(false);
|
||||
const [clicked, click] = useState(false);
|
||||
const [mode, setMode] = useState(false);
|
||||
const [down, setDown] = useState(false);
|
||||
const onFrame = (state, delta) => {
|
||||
sphere.current.rotation.x += delta;
|
||||
const v = (hovered ? state.mouse.x / 2 : 0);
|
||||
sphere.current.rotation.x = MathUtils.lerp(
|
||||
sphere.current.rotation.x,
|
||||
v,
|
||||
0.2
|
||||
);
|
||||
sphere.current.position.y = MathUtils.lerp(
|
||||
sphere.current.position.y,
|
||||
Math.sin(state.clock.elapsedTime / 1.5) / 6 +
|
||||
(hovered ? state.mouse.y / 2 : 0),
|
||||
0.2
|
||||
);
|
||||
};
|
||||
|
||||
// Subscribe this component to the render-loop, rotate the mesh every frame
|
||||
useFrame(onFrame);
|
||||
|
||||
// Springs for color and overall looks, this is state-driven animation
|
||||
// React-spring is physics based and turns static props into animated values
|
||||
const [{ wobble, coat, color, ambient, env }] = useSpring(
|
||||
{
|
||||
wobble: down ? 1.2 : hovered ? 1.05 : 1,
|
||||
coat: mode && !hovered ? 0.04 : 1,
|
||||
ambient: mode && !hovered ? 1.5 : 0.5,
|
||||
env: mode && !hovered ? 0.4 : 1,
|
||||
color: hovered ? "#E8B059" : mode ? "#202020" : "white",
|
||||
config: (n) =>
|
||||
n === "wobble" && hovered && { mass: 2, tension: 1000, friction: 10 },
|
||||
},
|
||||
[mode, hovered, down]
|
||||
);
|
||||
|
||||
return (
|
||||
<animated.mesh
|
||||
position={[0,0,-1]}
|
||||
ref={sphere}
|
||||
scale={wobble}
|
||||
onClick={(event) => click(!clicked)}
|
||||
onPointerOver={(event) => hover(true)}
|
||||
onPointerOut={(event) => hover(false)}
|
||||
onPointerDown={() => setDown(true)}
|
||||
onPointerUp={() => {
|
||||
setDown(false);
|
||||
// Toggle mode between dark and bright
|
||||
setMode(!mode);
|
||||
}}
|
||||
>
|
||||
<sphereGeometry args={[1, 32, 32]} />
|
||||
{/* <AnimatedMaterial color={color} envMapIntensity={env} clearcoat={coat} clearcoatRoughness={0} metalness={0.1} /> */}
|
||||
<meshNormalMaterial />
|
||||
</animated.mesh>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import "../App.css";
|
||||
import Blurb from "./Blurb";
|
||||
import React from 'react'
|
||||
import { OrbitControls } from '@react-three/drei'
|
||||
import { a } from '@react-spring/web'
|
||||
|
||||
export default function Splash() {
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<div className="hero min-h-screen bg-base-200">
|
||||
<div className="hero-content h-screen flex-col lg:flex-row-reverse w-screen">
|
||||
<div className="text-center lg:text-left">
|
||||
<h1 className="text-9xl font-bold mb-4">Noah</h1>
|
||||
<h1 className="text-9xl font-bold">Paige</h1>
|
||||
</div>
|
||||
<a.main className="h-full" >
|
||||
<Canvas className="canvas" dpr={[1, 2]}>
|
||||
<Blurb />
|
||||
<OrbitControls enablePan={false} enableZoom={false} maxAzimuthAngle={ Math.PI * 13 / 24 } minAzimuthAngle={ Math.PI * 11 / 24 } maxPolarAngle={Math.PI * 13 / 24} minPolarAngle={Math.PI * 11 / 24} />
|
||||
</Canvas>
|
||||
</a.main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import "../App.css";
|
||||
|
||||
function Splash() {
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<h1>Splash</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Splash;
|
||||
@@ -1,12 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import "../App.css";
|
||||
|
||||
function Work() {
|
||||
export default function Work() {
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<h1>Work</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Work;
|
||||
@@ -8,7 +8,7 @@ import MyFooter from "../Components/Footer";
|
||||
//todo:
|
||||
// - create 'screen' component that occupies a full screen
|
||||
|
||||
function App() {
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="overflow-scroll">
|
||||
<div className="snap-mandatory snap-y overflow-scroll h-screen bg-base-200">
|
||||
@@ -18,11 +18,12 @@ function App() {
|
||||
<div className="snap-center h-screen w-screen">
|
||||
<Work />
|
||||
</div>
|
||||
<div className="snap-center h-screen w-screen">
|
||||
<About />
|
||||
</div>
|
||||
<div className="snap-center h-screen w-screen">About</div>
|
||||
<MyFooter />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
+1
-1
@@ -3,7 +3,7 @@ module.exports = {
|
||||
mode: 'jit',
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
"./src/**/*.{js,jsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user