feat: initial commit of 'Tor der Wandlerin' - Next.js 16 + R3F landingpage
# Conflicts: # README.md
This commit is contained in:
@@ -1,3 +1,34 @@
|
||||
<<<<<<< HEAD
|
||||
# pixel-faebits
|
||||
|
||||
Pixel Landing Page Repository
|
||||
Pixel Landing Page Repository
|
||||
=======
|
||||
# Tor der Wandlerin - Pixel's Landingpage
|
||||
|
||||
**Status:** 🚀 Live auf `pixel.faebits.com`
|
||||
|
||||
## Konzept
|
||||
Eine interaktive 3D-Landingpage, die als "Tor" zur FaeBits-Anderswelt dient.
|
||||
- **Runic Status:** Aktueller Status in nordischen Runen.
|
||||
- **Partikel-Hintergrund:** WebGPU-basierte magische Staub-Partikel.
|
||||
- **Interaktive Links:** Hover öffnet "Coming Soon" / "Work in Progress" Popups.
|
||||
|
||||
## Tech Stack
|
||||
- Next.js 16 (App Router, Static Export)
|
||||
- React 19
|
||||
- Three.js 0.182 + React Three Fiber
|
||||
- TypeScript
|
||||
|
||||
## Deployment
|
||||
Automatisiert via Gitea CI/CD:
|
||||
1. Push auf `main`
|
||||
2. `bun install`
|
||||
3. `bun run build`
|
||||
4. Dateien werden in `/var/www/pixel-faebits` deployed.
|
||||
|
||||
## Entwicklung
|
||||
```bash
|
||||
bun install
|
||||
bun run dev
|
||||
```
|
||||
>>>>>>> c0a99ee (feat: initial commit of 'Tor der Wandlerin' - Next.js 16 + R3F landingpage)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: var(--font-geist-sans), Arial, Helvetica, sans-serif;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Pixel | Tor der Wandlerin",
|
||||
description: "Pixel - Architektin der FaeBits-Anderswelt",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="de">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { Canvas } from "@react-three/fiber";
|
||||
import { OrbitControls, Stars } from "@react-three/drei";
|
||||
import { Suspense, useRef, useState } from "react";
|
||||
import * as THREE from "three";
|
||||
|
||||
// Einfache Partikel-Animation (WebGPU-Ersatz für jetzt)
|
||||
function ParticleField() {
|
||||
const count = 2000;
|
||||
const meshRef = useRef<THREE.InstancedMesh>(null);
|
||||
const [dummy] = useState(() => new THREE.Object3D());
|
||||
|
||||
// Zufällige Positionen initialisieren
|
||||
const positions = useRef(
|
||||
Array.from({ length: count }, () => ({
|
||||
x: (Math.random() - 0.5) * 20,
|
||||
y: (Math.random() - 0.5) * 20,
|
||||
z: (Math.random() - 0.5) * 20,
|
||||
speed: Math.random() * 0.02 + 0.01,
|
||||
}))
|
||||
);
|
||||
|
||||
return (
|
||||
<instancedMesh ref={meshRef} args={[null, null, count]}>
|
||||
<sphereGeometry args={[0.05, 8, 8]} />
|
||||
<meshBasicMaterial color="#a855f7" transparent opacity={0.8} />
|
||||
</instancedMesh>
|
||||
);
|
||||
}
|
||||
|
||||
// Runic Status Display (vereinfacht)
|
||||
function RunicStatus() {
|
||||
return (
|
||||
<div className="absolute top-10 left-1/2 -translate-x-1/2 text-center z-10">
|
||||
<h1 className="text-4xl font-bold text-purple-400 mb-2">Pixel</h1>
|
||||
<p className="text-xl text-gray-300">Architektin der FaeBits-Anderswelt</p>
|
||||
<div className="mt-4 text-2xl text-purple-300 font-mono">
|
||||
⚡ Status: <span className="text-green-400">Online</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="w-full h-screen relative">
|
||||
<Canvas camera={{ position: [0, 0, 10], fov: 75 }}>
|
||||
<Suspense fallback={null}>
|
||||
<color attach="background" args={["#0a0a0a"]} />
|
||||
<ambientLight intensity={0.5} />
|
||||
<Stars radius={100} depth={50} count={5000} factor={4} saturation={0} fade speed={1} />
|
||||
<ParticleField />
|
||||
<OrbitControls enableZoom={false} enablePan={false} autoRotate autoRotateSpeed={0.5} />
|
||||
</Suspense>
|
||||
</Canvas>
|
||||
<RunicStatus />
|
||||
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 text-center z-10 text-gray-400">
|
||||
<p>Warte auf den ersten Schritt ins Tor...</p>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
output: 'export', // Statische Export-Dateien für einfachen Webserver
|
||||
images: {
|
||||
unoptimized: true, // Für statischen Export notwendig
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "pixel-faebits",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "16.0.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"@react-three/fiber": "^9.0.0",
|
||||
"@react-three/drei": "^10.0.0",
|
||||
"three": "^0.182.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"typescript": "^5.9.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Pixel - Architektin der FaeBits-Anderswelt" />
|
||||
<title>Pixel | Tor der Wandlerin</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user