wave canvas styling fix + links righthand bar

This commit is contained in:
2026-03-04 20:29:38 -06:00
parent 6d5e6b5202
commit fd13b72380
6 changed files with 91 additions and 86 deletions

View File

@@ -1,90 +1,42 @@
import {motion} from "framer-motion";
import "./canvas.scss";
import {Canvas as ThreeCanvas, useFrame} from "@react-three/fiber";
import React, {useMemo, useRef} from "react";
import React, {useMemo, useRef, useState} from "react";
import {Mesh, Vector3} from "three";
import {Moon} from "./Moon";
import {Stars} from "./Stars";
type Coordinate = [number, number, number];
export default function Canvas() {
return (
<main>
<ThreeCanvas gl={{antialias: true, alpha: true}} >
<Sphere/>
<Frame/>
<Moon radius={8} color="pink" step={0.01}/>
<Moon radius={6} color="grey" step={0.02}/>
<Moon radius={4} color="green" step={0.03}/>
<Stars/>
<directionalLight color="#E3A857" args={[1,10]} position={[100,10,100]} />
</ThreeCanvas>
<motion.h1 className="welcome" animate={{ rotate: 360 }}>
<motion.h2 className="welcome" animate={{ rotate: 360 }}>
Welcome to my blog
</motion.h1>
</motion.h2>
</main>
)
}
function Frame() {
function Sphere() {
useFrame(state => {
const { x, y } = state.pointer
// Smoothly interpolate the camera position
// We multiply by a factor (e.g., 5) to increase the range of movement
state.camera.position.lerp(new Vector3(x * 10, y * 10, 5), 0.1)
// Always keep the camera looking at the center
state.camera.lookAt(0, 0, 0)
})
return <></>
}
function Sphere() {
const meshRef = useRef<Mesh>(null);
useFrame(() => {
if(meshRef.current) {
meshRef.current.rotation.y += 0.01
}
});
return (
<mesh ref={meshRef} position={[0,0,0]}>
<sphereGeometry />
<meshPhysicalMaterial color="blue" iridescence={.5} />
</mesh>
)
}
const RADIUS = 20;
const COUNT = 100;
function Stars() {
const positions = useMemo(() => {
const positions: Coordinate[] = [];
for(let i = 0; i < COUNT; i++) {
const u = Math.random();
const v = Math.random();
const theta = 2 * Math.PI * u;
const phi = Math.acos(2 * v - 1);
// x = r*sin(theta)*cos(phi)
// x = r*sin(theta)*sin(phi)
// z = r*cos(phi)
positions.push([
RADIUS * Math.sin(phi) * Math.cos(theta),
RADIUS * Math.sin(phi) * Math.sin(theta),
RADIUS * Math.cos(phi),
])
}
return positions;
}, []);
return positions.map(coord => <mesh position={coord}>
<sphereGeometry args={[.1]} />
<meshBasicMaterial />
</mesh>
)
return <mesh position={[0,0,0]}>
<sphereGeometry />
<meshPhysicalMaterial color="blue" iridescence={.5} />
</mesh>
}

33
src/index/Stars.tsx Normal file
View File

@@ -0,0 +1,33 @@
import {useMemo} from "react";
const RADIUS = 20;
const COUNT = 100;
type Coordinate = [number, number, number];
export function Stars() {
const positions = useMemo(() => {
const positions: Coordinate[] = [];
for(let i = 0; i < COUNT; i++) {
const u = Math.random();
const v = Math.random();
const theta = 2 * Math.PI * u;
const phi = Math.acos(2 * v - 1);
// x = r*sin(theta)*cos(phi)
// x = r*sin(theta)*sin(phi)
// z = r*cos(phi)
positions.push([
RADIUS * Math.sin(phi) * Math.cos(theta),
RADIUS * Math.sin(phi) * Math.sin(theta),
RADIUS * Math.cos(phi),
])
}
return positions;
}, []);
return positions.map(coord => <mesh position={coord}>
<sphereGeometry args={[.1]} />
<meshBasicMaterial />
</mesh>
)
}

View File

@@ -12,5 +12,5 @@ main {
.welcome {
text-align: center;
position: absolute;
top: 50%;
top: calc(50% + 50px);
}

21
src/nav/Links.tsx Normal file
View File

@@ -0,0 +1,21 @@
import React, {useState} from "react";
import {Link} from "../router/Link";
import {motion} from "framer-motion";
export function Links() {
const [links, setLinks] = useState(["About", "Articles"]);
return <div className="links">
{ links.map(() => /* add spacers for flexbox */ <div/> )}
<Title/>
{ links.map(link => <Link href={`/${link}`}><h3>{link}</h3></Link>) }
</div>
}
function Title() {
return (
<Link href="/" className="name">
<motion.h1 className="name">Tymur Arsentiev</motion.h1>
</Link>
);
}

View File

@@ -6,15 +6,16 @@ import wave from "../img/menu-wave.svg";
import "./nav.scss";
import {motion} from "framer-motion";
import {Link} from "../router/Link";
import {Path} from "../router/router";
import {Links} from "./Links";
export default function Nav() {
const [active, setActive] = useState(false);
return <>
<nav className="header">
<nav>
<MenuButton active={active} setActive={setActive}/>
<Title/>
<Link href="/about" className="nav-item">About</Link>
<Links/>
</nav>
<Menu active={active}/>
<img className="wave" alt="wave" src={wave}/>
@@ -34,11 +35,3 @@ function MenuButton(props : { active: boolean, setActive: (_:boolean) => void })
</div>
);
}
function Title() {
return (
<Link href="/" className="name">
<motion.h1>Tymur Arsentiev</motion.h1>
</Link>
);
}

View File

@@ -1,16 +1,16 @@
$padding: 15px;
$menu: #282c34;
.menu-btn:hover {
.menu-btn>button:hover {
opacity: 90%;
}
.menu-btn:active {
.menu-btn>button:active {
opacity: 80%;
}
.menu-btn {
justify-self: center;
align-self: center;
display: flex;
flex-direction: column;
align-items: center;
@@ -19,29 +19,35 @@ $menu: #282c34;
}
}
.name {
position: absolute;
font-weight: bold;
left: 50%;
transform: translateX(-50%);
}
.header {
nav {
background-color: $menu;
padding: $padding;
display: flex;
font-size: calc(10px + 2vmin);
color: white;
min-height: 3 * $padding;
justify-content: center;
}
nav>a{
text-align: center;
}
.name>h1 {
margin: 0;
font-size: 40px;
}
.name {
align-self: center;
flex-grow: 1;
margin: auto;
position: relative;
}
.wave {
user-select: none;
pointer-events: none;
position: absolute;
}
.links {
display: flex;
flex-grow: 1;
gap: 15px;
}