-
- To get started, edit the page.tsx file.
-
-
- Looking for a starting point or more instructions? Head over to{" "}
-
- Templates
- {" "}
- or the{" "}
-
- Learning
- {" "}
- center.
-
+import Title from "@app/components/sections/Title";
+import Navbar from "@app/components/sections/Navbar";
+import About from "@app/components/sections/About";
+import Skills from "@app/components/sections/Skills";
+import Projects from "@app/components/sections/Projects";
+
+import dynamic from 'next/dynamic';
+
+import { useState } from "react";
+
+import '@app/lib/i18n';
+
+export type MenuState = {
+ about: boolean;
+ skills: boolean;
+ projects: boolean;
+};
+
+const Three = dynamic(() => import('@app/components/three/Three'), {
+ ssr: false,
+ loading: () =>
+});
+
+export default function App() {
+ const [state, setState] = useState
({
+ about: false,
+ skills: false,
+ projects: false,
+ });
+
+ const closeSection = (key: keyof MenuState) => {
+ setState(prev => ({ ...prev, [key]: false }));
+ };
+
+ const isOpen = Object.values(state).some(value => value === true);
+
+ return (
+
+
+
+
+ closeSection("about")} />
+ closeSection("skills")} />
+ closeSection("projects")} />
-
-
-
- );
-}
+ );
+}
\ No newline at end of file
diff --git a/app/three/animQueue.ts b/app/three/animQueue.ts
new file mode 100644
index 0000000..1cc57b6
--- /dev/null
+++ b/app/three/animQueue.ts
@@ -0,0 +1,92 @@
+import * as THREE from "three";
+import Animation from "./animation";
+import assets from "@app/data/assets.json"
+
+const ANIMATION_ASSETS = assets.animations;
+
+export default class AnimationQueue {
+ private animation: Animation;
+ private queue: THREE.AnimationAction[] = [];
+ private currentAction: THREE.AnimationAction | null = null;
+ private mixer: THREE.AnimationMixer;
+ private randomIntervalId: number | null = null;
+
+ constructor(animation: Animation) {
+ this.mixer = animation.getMixer();
+ this.animation = animation;
+ }
+
+ public onqueue(action: THREE.AnimationAction) {
+ this.queue.push(action);
+ this.tryPlayNext();
+ }
+
+ public replaceFirst(action: THREE.AnimationAction) {
+ this.queue.unshift(action);
+ this.tryPlayNext(true);
+ }
+
+ public async tryPlayNext(force: boolean = false) {
+ if (this.currentAction) {
+ if (force) {
+ this.currentAction.fadeOut(this.animation.getFadeout());
+ this.currentAction = null;
+ } else {
+ if ((this.currentAction as any).isBasicClone) {
+ this.currentAction.fadeOut(this.animation.getFadeout());
+ this.currentAction = null;
+ } else {
+ return;
+ }
+ }
+ }
+
+ if (!this.queue.length) this.queue.push(this.animation.getBasicAction());
+
+ const NEXTACTION = this.queue.shift()!;
+ NEXTACTION.reset();
+ NEXTACTION.setLoop(THREE.LoopOnce, 1);
+ NEXTACTION.clampWhenFinished = true;
+ NEXTACTION.fadeIn(this.animation.getFadein()).play();
+
+ const onFinish = (e: any) => {
+ if (e.action === this.currentAction) {
+ if (this.currentAction) this.currentAction.fadeOut(this.animation.getFadeout());
+ this.mixer.removeEventListener("finished", onFinish);
+ this.currentAction = null;
+ return this.tryPlayNext();
+ }
+ };
+
+ this.currentAction = NEXTACTION;
+ this.mixer.addEventListener("finished", onFinish);
+ }
+
+ public startRandom() {
+ if (this.randomIntervalId !== null) return;
+
+ this.randomIntervalId = window.setInterval(async () => {
+ if (!this.mixer) return;
+ const RANDOMINDEX = Math.floor(Math.random() * ANIMATION_ASSETS.length);
+ this.onqueue(await this.animation.loadAnimation(ANIMATION_ASSETS[RANDOMINDEX]));
+ }, 30_000);
+ }
+
+ public stopRandom() {
+ if (this.randomIntervalId !== null) {
+ clearInterval(this.randomIntervalId);
+ this.randomIntervalId = null;
+ }
+ }
+
+ public clearQueue() {
+ this.queue = [];
+ }
+
+ public stop() {
+ this.clearQueue();
+ this.currentAction?.fadeOut(this.animation.getFadeout());
+ this.currentAction = null;
+ this.stopRandom();
+ }
+}
\ No newline at end of file
diff --git a/app/three/animation.ts b/app/three/animation.ts
new file mode 100644
index 0000000..52b17b6
--- /dev/null
+++ b/app/three/animation.ts
@@ -0,0 +1,81 @@
+import * as THREE from "three";
+import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+
+export default class Animation {
+ private mixer: THREE.AnimationMixer;
+ private loader: GLTFLoader;
+
+ private actions: Map