304 lines
13 KiB
HTML
304 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
|
||
<head>
|
||
<link rel="icon" href="images/generated_two_no_background.png">
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Marvins Portfolio</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=DM+Sans:wght@300;400;500&display=swap"
|
||
rel="stylesheet" />
|
||
<link rel="stylesheet" href="header_style.css">
|
||
</head>
|
||
|
||
<body>
|
||
|
||
<!-- MOBILE BAR -->
|
||
<div class="mobile-bar">
|
||
<p class="mobile-logo">Marvin Kraußer</p>
|
||
<button class="mobile-btn" onclick="openDrawer()">Menü ↓</button>
|
||
</div>
|
||
<div class="mobile-drawer" id="drawer">
|
||
<div class="mobile-drawer-head">
|
||
<span>Navigation</span>
|
||
<button class="mobile-btn" onclick="closeDrawer()">✕ Schließen</button>
|
||
</div>
|
||
<div id="drawer-links"></div>
|
||
</div>
|
||
|
||
<!-- SIDEBAR -->
|
||
<aside class="sidebar">
|
||
<div class="sidebar-top">
|
||
<p class="sidebar-name">Marvin Kraußer</p>
|
||
<span class="sidebar-sub">Student B.Sc.<br>Medieninformatik<br>Universität Ulm</span>
|
||
</div>
|
||
<nav class="sidebar-nav" id="sidebar-nav"></nav>
|
||
<div class="sidebar-bottom">
|
||
<a href="https://github.com/marvinkrausser" target="_blank">github.com/marvinkrausser ↗</a>
|
||
<a href="mailto:marvin.krausser277@gmail.com">marvin.krausser277@gmail.com</a>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- MAIN -->
|
||
<main class="main">
|
||
|
||
<!-- HERO -->
|
||
<section id="hero" data-nav="Start" class="hero-section">
|
||
<h1>Hallo!</h1>
|
||
<p>Ich studiere Medieninformatik im Bachelor und habe im Rahmen meines Studiums praktische Erfahrungen in
|
||
verschiedenen Projekten gesammelt. Besonders gerne arbeite ich an Projekten, in denen ich neue Technologien
|
||
ausprobieren und vertiefen kann, aktuell vor allem im Bereich Machine Learning.</p>
|
||
<div class="hero-links">
|
||
<a class="text-link" href="https://github.com/marvinkrausser" target="_blank">GitHub ↗</a>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- PROJEKTE werden per JS eingefügt -->
|
||
<div id="projects-root"></div>
|
||
|
||
</main>
|
||
|
||
|
||
<script>
|
||
/* ══════════════════════════════════════════════════════
|
||
DATEN
|
||
Jedes Projekt hat:
|
||
- id, title, description, tags, stars, forks, status,
|
||
github, demo
|
||
- blocks[]: Array von { label, heading, text, image }
|
||
image kann ein URL-String sein oder null (= Platzhalter)
|
||
══════════════════════════════════════════════════════ */
|
||
const PROJECTS = [
|
||
{
|
||
id: "pixelcnn",
|
||
title: "PixelCNN Bildgeneration",
|
||
description: "Autoregressives Deep Learning-Modell basierend auf PixelCNN zur generativen Bildsynthese mit PyTorch.",
|
||
tags: ["Python", "PyTorch", "CUDA"],
|
||
github: "https://github.com/MarvinKrausser/pixelcnn",
|
||
blocks: [
|
||
{
|
||
label: "",
|
||
heading: "Bildgenerierung mit Deep Learning",
|
||
text: "Das Modell verwendet Convolutional Neural Networks und Deep Learning zur Generierung von Bildern. Als Trainingsdatensatz wurden die MNIST-Digits verwendet und mittels CUDA auf einer GPU trainiert. Zusätzlich kann das Modell gezielt mit Klassen als Eingabe gesteuert werden, wie im Beispiel mit den Ziffern von 0 bis 9 gezeigt. Aufgrund begrenzter Ressourcen ist mein Modell auf Datensätze mit niedriger Komplexität beschränkt.",
|
||
image: "images/mnist_conditioned_gen_results_cropped.png",
|
||
},
|
||
{
|
||
label: "",
|
||
heading: "Seminararbeit",
|
||
text: "Das Projekt entstand begleitend zusätzlich zu einer <a class=\"text-link\" href=\"pdf_viewer.html?file=Seminararbeit.pdf\">Seminararbeit ↗</a> mit <a class=\"text-link\" href=\"pdf_viewer.html?file=Seminararbeit_Vortrag.pdf\">Vortrag ↗</a>.",
|
||
image: "images/seminararbeit.png",
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: "boardgame",
|
||
title: "Online Multiplayer Game",
|
||
description: "Ein Online-Multiplayer-Brettspiel mit Server, Spielclient und KI-Spieler entstanden als Gruppenarbeit mit 5 weiteren Kommilitonen.",
|
||
tags: ["C#", ".net", "Unity", "Docker"],
|
||
github: "https://github.com/MarvinKrausser/BoardgameServer",
|
||
blocks: [
|
||
{
|
||
label: "",
|
||
heading: "Client",
|
||
text: "Der Game Client wurde mit Unity und C# entwickelt und basiert auf einem rundenbasierten Bewegungs- und Kartensystem. Sämtliche Designs wurden eigenständig erstellt. Auch ein <a class=\"text-link\" href=\"pdf_viewer.html?file=Team23_Benutzerhandbuch_BoC.pdf\">Benutzerhandbuch ↗</a> und <a class=\"text-link\" href=\"pdf_viewer.html?file=Team23_Entwicklerhandbuch_BoC.pdf\">Entwicklerhandbuch ↗</a> wurden erstellt.",
|
||
image: "images/boardgame_demo.png",
|
||
},
|
||
{
|
||
label: "",
|
||
heading: "Server",
|
||
text: "Der Server wurde in C# mit einer mehrschichtigen Architektur umgesetzt, die eine klare Trennung zwischen WebSocket-Verbindungsmanagement und spielbezogener Logik (z. B. Spielerzuweisung und Sitzungsverwaltung) ermöglicht. Die Kommunikation zwischen Server und Client wurde durch ein definiertes JSON-Schema standardisiert, um die Kompatibilität und Austauschbarkeit der von verschiedenen Entwicklerteams entwickelten Komponenten sicherzustellen.",
|
||
image: "images/boardgame_connection.png",
|
||
},
|
||
{
|
||
label: "",
|
||
heading: "KI-Spieler",
|
||
text: "Zusätzlich wurden mehrere regelbasierte KI-Spieler implementiert, die in das System integriert werden können. Die Codequalität wurde durch automatisierte Tests sichergestellt. Die Zusammenarbeit im Team erfolgte nach agilen Methoden (Scrum), und Server sowie KI wurden containerbasiert mit Docker bereitgestellt.",
|
||
image: "images/boardgame_ai.png",
|
||
}
|
||
]
|
||
},
|
||
{
|
||
id: "towerdefense",
|
||
title: "Towerdefense Game",
|
||
description: "Singleplayer Towerdefense Game mit Editor entstanden im Rahmen einer Vorlesung als Einzelprojekt.",
|
||
tags: ["C#", "Unity"],
|
||
github: "https://github.com/MarvinKrausser/TowerDefenseGame",
|
||
blocks: [
|
||
{
|
||
label: "",
|
||
heading: "Singleplayer Tower Defense Game",
|
||
text: "Einfaches Tower-Defense-Spiel das von Grund auf mit eigenen Designs erstellt wurde. Nutzt den A*-Algorithmus zur Berechnung des Laufweges. Ein Demo-Build ist im Github Repository vorhanden.",
|
||
image: "images/towerdefense_demo.png",
|
||
},
|
||
],
|
||
},
|
||
];
|
||
|
||
|
||
/* ══════════════════════════════════════════════════════
|
||
WEB COMPONENT: <project-section>
|
||
Rendert einen kompletten Projekt-Abschnitt mit
|
||
Header, alternierenden Content-Blocks und Link-Zeile.
|
||
══════════════════════════════════════════════════════ */
|
||
class ProjectSection extends HTMLElement {
|
||
connectedCallback() {
|
||
const p = JSON.parse(this.getAttribute("data-project") || "{}");
|
||
if (!p.id) return;
|
||
|
||
const tags = p.tags.map(t => `<span class="tag">${t}</span>`).join("");
|
||
|
||
const blocks = (p.blocks || []).map((b, i) => {
|
||
const flip = i % 2 === 1 ? " flip" : "";
|
||
const imageContent = b.image
|
||
? `<img src="${b.image}" alt="${b.heading}" loading="lazy" />`
|
||
: `<div class="placeholder">
|
||
<div class="placeholder-icon">◻</div>
|
||
<div class="placeholder-label">Bild einfügen</div>
|
||
</div>`;
|
||
|
||
return `
|
||
<div class="content-block${flip}">
|
||
<div class="block-image">${imageContent}</div>
|
||
<div class="block-text">
|
||
<p class="block-label">${b.label}</p>
|
||
<h3>${b.heading}</h3>
|
||
<p>${b.text}</p>
|
||
</div>
|
||
</div>`;
|
||
}).join("");
|
||
|
||
this.innerHTML = `
|
||
<section class="project-section" id="${p.id}" data-nav="${p.title}">
|
||
<div class="section-head">
|
||
<h2 class="section-title">${p.title}</h2>
|
||
<p class="section-desc">${p.description}</p>
|
||
<div class="meta-row">
|
||
<div class="tags">${tags}</div>
|
||
</div>
|
||
</div>
|
||
<div class="content-blocks">${blocks}</div>
|
||
<div class="links-row">
|
||
<a class="text-link" href="${p.github}" target="_blank">Auf GitHub ansehen ↗</a>
|
||
</div>
|
||
</section>`;
|
||
}
|
||
}
|
||
customElements.define("project-section", ProjectSection);
|
||
|
||
|
||
/* ══════════════════════════════════════════════════════
|
||
RENDER
|
||
══════════════════════════════════════════════════════ */
|
||
const root = document.getElementById("projects-root");
|
||
PROJECTS.forEach(p => {
|
||
const el = document.createElement("project-section");
|
||
el.setAttribute("data-project", JSON.stringify(p));
|
||
root.appendChild(el);
|
||
});
|
||
|
||
|
||
/* ══════════════════════════════════════════════════════
|
||
NAV AUFBAUEN + ACTIVE STATE
|
||
══════════════════════════════════════════════════════ */
|
||
function buildNav() {
|
||
const sidebarNav = document.getElementById("sidebar-nav");
|
||
const drawerLinks = document.getElementById("drawer-links");
|
||
sidebarNav.innerHTML = "";
|
||
drawerLinks.innerHTML = "";
|
||
|
||
// Statische Sections
|
||
const staticSections = [
|
||
{ id: "hero", label: "Hallo!", group: null, idx: "00" },
|
||
];
|
||
|
||
// Projekt-Sections nach dem Rendern einsammeln
|
||
const projectSections = PROJECTS.map((p, i) => ({
|
||
id: p.id,
|
||
label: p.title,
|
||
idx: String(i + 1).padStart(2, "0"),
|
||
group: "Projekte",
|
||
}));
|
||
|
||
const allSections = [...staticSections, ...projectSections];
|
||
|
||
// Gruppen
|
||
let lastGroup = "__none__";
|
||
allSections.forEach(s => {
|
||
if (s.group !== lastGroup) {
|
||
lastGroup = s.group;
|
||
if (s.group) {
|
||
const lbl = document.createElement("span");
|
||
lbl.className = "nav-group-label";
|
||
lbl.textContent = s.group;
|
||
sidebarNav.appendChild(lbl);
|
||
}
|
||
}
|
||
|
||
// Sidebar link
|
||
const a = document.createElement("a");
|
||
a.className = "nav-link";
|
||
a.href = "#";
|
||
a.onclick = (e) => {
|
||
e.preventDefault();
|
||
scrollToSection(s.id);
|
||
};
|
||
a.dataset.target = s.id;
|
||
if (s.idx) {
|
||
const num = document.createElement("span");
|
||
num.className = "idx"; num.textContent = s.idx;
|
||
a.appendChild(num);
|
||
}
|
||
a.appendChild(document.createTextNode(s.label));
|
||
sidebarNav.appendChild(a);
|
||
|
||
// Mobile drawer link
|
||
const ma = document.createElement("a");
|
||
ma.href = "#";
|
||
ma.onclick = (e) => {
|
||
e.preventDefault();
|
||
scrollToSection(s.id);
|
||
closeDrawer();
|
||
};
|
||
ma.textContent = s.idx ? `${s.idx} – ${s.label}` : s.label;
|
||
drawerLinks.appendChild(ma);
|
||
});
|
||
}
|
||
|
||
function setupObserver() {
|
||
const observer = new IntersectionObserver(entries => {
|
||
entries.forEach(e => {
|
||
if (e.isIntersecting) {
|
||
document.querySelectorAll(".nav-link").forEach(a =>
|
||
a.classList.toggle("active", a.dataset.target === e.target.id)
|
||
);
|
||
}
|
||
});
|
||
}, { rootMargin: "0px 0px -55% 0px", threshold: 0 });
|
||
|
||
document.querySelectorAll("[data-nav]").forEach(s => observer.observe(s));
|
||
}
|
||
|
||
// Custom elements render async – short timeout before building nav
|
||
setTimeout(() => { buildNav(); setupObserver(); }, 120);
|
||
|
||
|
||
/* ══════════════════════════════════════════════════════
|
||
MOBILE DRAWER
|
||
══════════════════════════════════════════════════════ */
|
||
function openDrawer() { document.getElementById("drawer").classList.add("open"); }
|
||
function closeDrawer() { document.getElementById("drawer").classList.remove("open"); }
|
||
|
||
function scrollToSection(id) {
|
||
document.getElementById(id).scrollIntoView({
|
||
behavior: 'smooth'
|
||
});
|
||
|
||
history.replaceState(null, null, window.location.pathname);
|
||
}
|
||
</script>
|
||
</body>
|
||
|
||
</html> |