new file: Dockerfile new file: README.md new file: app.py new file: index.html new file: projekte des/PROJECT_DESCRIPTION.txt new file: projekte des/WEBSITE_DESCRIPTION.md new file: projekte des/website_project_description_en.txt new file: requirements.txt new file: script.js new file: static/css/styles.css new file: static/js/script.js new file: styles.css new file: templates/about.html new file: templates/base.html new file: templates/contact.html new file: templates/index.html new file: templates/minecraft.html new file: templates/project_detail.html new file: templates/projects.html
99 lines
3.4 KiB
JavaScript
99 lines
3.4 KiB
JavaScript
// Mirrors root script.js for Flask static serving
|
|
// Mobile Navigation Toggle
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const hamburger = document.querySelector('.hamburger');
|
|
const navLinks = document.querySelector('.nav-links');
|
|
|
|
if (hamburger && navLinks) {
|
|
hamburger.addEventListener('click', function() {
|
|
navLinks.classList.toggle('active');
|
|
hamburger.classList.toggle('active');
|
|
});
|
|
|
|
document.querySelectorAll('.nav-links a').forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
navLinks.classList.remove('active');
|
|
hamburger.classList.remove('active');
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
// Smooth scrolling for navigation links
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
const href = this.getAttribute('href');
|
|
if (href && href.startsWith('#')) {
|
|
e.preventDefault();
|
|
const target = document.querySelector(href);
|
|
if (target) {
|
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// Navbar background on scroll
|
|
window.addEventListener('scroll', function() {
|
|
const navbar = document.querySelector('.navbar');
|
|
if (navbar) {
|
|
navbar.style.background = window.scrollY > 100 ? 'rgba(10, 10, 10, 0.98)' : 'rgba(10, 10, 10, 0.95)';
|
|
}
|
|
});
|
|
|
|
// Intersection Observer for animations
|
|
const observeElements = () => {
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.style.opacity = '1';
|
|
entry.target.style.transform = 'translateY(0)';
|
|
}
|
|
});
|
|
}, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' });
|
|
|
|
document.querySelectorAll('.project-card, .tech-item, .contact-card').forEach((el, i) => {
|
|
el.style.opacity = '0';
|
|
el.style.transform = 'translateY(30px)';
|
|
el.style.transition = `opacity 0.6s ease ${i * 0.1}s, transform 0.6s ease ${i * 0.1}s`;
|
|
observer.observe(el);
|
|
});
|
|
};
|
|
|
|
document.addEventListener('DOMContentLoaded', observeElements);
|
|
|
|
// Hover effects for project cards
|
|
document.querySelectorAll('.project-card').forEach(card => {
|
|
card.addEventListener('mouseenter', function() {
|
|
this.style.transform = 'translateY(-10px) scale(1.02)';
|
|
});
|
|
card.addEventListener('mouseleave', function() {
|
|
this.style.transform = 'translateY(0) scale(1)';
|
|
});
|
|
});
|
|
|
|
// Active state to navigation links based on scroll position
|
|
const updateActiveNav = () => {
|
|
const sections = document.querySelectorAll('section[id]');
|
|
const navLinks = document.querySelectorAll('.nav-links a[href^="#"]');
|
|
let current = '';
|
|
sections.forEach(section => {
|
|
if (pageYOffset >= section.offsetTop - 200) {
|
|
current = section.getAttribute('id');
|
|
}
|
|
});
|
|
navLinks.forEach(link => {
|
|
link.classList.toggle('active', link.getAttribute('href') === `#${current}`);
|
|
});
|
|
};
|
|
|
|
window.addEventListener('scroll', updateActiveNav);
|
|
|
|
// Inject minimal CSS for active nav indicator
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
.nav-links a.active { color: #00d4ff !important; }
|
|
.nav-links a.active::before { width: 100% !important; }
|
|
`;
|
|
document.head.appendChild(style);
|