Files
douwco_website/public/assets/js/index.js
T
douwe c5dca7bb54 Implement games section with expandable cards
- Added responsive grid of game cards with dark-teal background
- Each card has: rounded gray image placeholder, white title, gray description, orange year badge
- Click to expand: shows full description + centered play button
- Play button opens game in new window
- Flat minimal design consistent with portfolio style
- Created placeholder images for 6 games
2026-09-25 11:25:58 +02:00

264 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
document.addEventListener('DOMContentLoaded', () => {
setupNavigationHighlight();
setupNavigationClicks();
setupCopyright();
setupProjectCards();
setupAppsCarousel();
setupGameCards();
});
function setupNavigationHighlight() {
const sections = document.querySelectorAll('.scroll_section');
const navOptions = document.querySelectorAll('.nav_option');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const sectionId = entry.target.id;
window.history.replaceState(null, null, `#${sectionId}`);
navOptions.forEach(link => {
link.classList.toggle('active', link.getAttribute('href') === `#${sectionId}`);
});
}
});
}, {
threshold: 0.5
});
sections.forEach(section => observer.observe(section));
}
function setupNavigationClicks() {
document.querySelectorAll('.nav_option').forEach(link => {
link.addEventListener('click', (event) => {
event.preventDefault();
const targetId = link.getAttribute('href');
const targetSection = document.querySelector(targetId);
if (targetSection) {
targetSection.scrollIntoView({
behavior: 'smooth'
});
}
});
});
}
function setupCopyright() {
document.getElementById('copyright').textContent = `© ${new Date().getFullYear()} - douwco.be`;
}
function setupProjectCards() {
const projectCards = document.querySelectorAll('.project-card-new');
projectCards.forEach(card => {
const shortDesc = card.querySelector('.project-card-short-desc');
const fullContent = card.querySelector('.project-card-content');
const toggleIcon = card.querySelector('.toggle-icon');
if (!shortDesc || !fullContent || !toggleIcon) return;
const toggleCard = (event) => {
if (event.target.tagName === 'A') {
return;
}
const isExpanded = card.classList.contains('expanded');
if (isExpanded) {
card.classList.remove('expanded');
toggleIcon.textContent = '+';
} else {
card.classList.add('expanded');
toggleIcon.textContent = '−';
}
event.stopPropagation();
};
card.addEventListener('click', toggleCard);
toggleIcon.addEventListener('click', toggleCard);
});
}
function setupAppsCarousel() {
const carousel = document.getElementById('apps-carousel');
const carouselContainer = carousel ? carousel.closest('.carousel-container-full') : null;
const items = document.querySelectorAll('#apps-carousel .carousel-item-full');
const prevBtn = carouselContainer ? carouselContainer.querySelector('.carousel-nav-full.prev') : null;
const nextBtn = carouselContainer ? carouselContainer.querySelector('.carousel-nav-full.next') : null;
const screenshotImg = document.getElementById('apps-screenshot-img');
const description = document.getElementById('apps-description');
const title = document.getElementById('apps-title');
const goToAppBtn = document.getElementById('apps-link');
if (!carousel || items.length === 0) return;
const services = [
{
name: 'Gitea',
screenshot: '/assets/img/gitea_screenshot.png',
description: 'Self-hosted Git service with a simple and lightweight interface.',
url: 'https://gitea.douwco.be'
},
{
name: 'Nextcloud',
screenshot: '/assets/img/nextcloud_screenshot.png',
description: 'Private file hosting and collaboration platform with powerful features.',
url: 'https://nextcloud.douwco.be'
},
{
name: 'Penpot',
screenshot: '/assets/img/penpot_screenshot.png',
description: 'Open-source design and prototyping platform for teams.',
url: 'https://penpot.douwco.be'
},
{
name: 'Beszel',
screenshot: '/assets/img/beszel_screenshot.png',
description: 'Custom service management and monitoring dashboard.',
url: 'https://beszel.douwco.be'
},
{
name: 'Wattif',
screenshot: '/assets/img/wattif_screenshot.png',
description: 'Energy monitoring and analysis tool for smart homes.',
url: 'https://wattif.douwco.be'
}
];
let currentIndex = 0;
let isAnimating = false;
function updateCarousel() {
if (isAnimating) return;
isAnimating = true;
items.forEach((item, index) => {
const isSelected = index === currentIndex;
item.setAttribute('data-selected', isSelected.toString());
});
const service = services[currentIndex];
if (description) description.classList.remove('active');
if (screenshotImg) screenshotImg.classList.remove('active');
if (title) title.classList.remove('active');
if (goToAppBtn) goToAppBtn.classList.remove('active');
setTimeout(() => {
if (screenshotImg) {
screenshotImg.src = service.screenshot;
}
if (title) {
title.textContent = service.name;
}
if (description) {
description.innerHTML = `<p>${service.description}</p>`;
}
if (goToAppBtn) {
goToAppBtn.href = service.url;
}
setTimeout(() => {
if (description) description.classList.add('active');
if (screenshotImg) screenshotImg.classList.add('active');
if (title) title.classList.add('active');
if (goToAppBtn) goToAppBtn.classList.add('active');
isAnimating = false;
}, 50);
}, 300);
}
function rotateCarousel(direction) {
if (isAnimating) return;
currentIndex = (currentIndex + direction + items.length) % items.length;
updateCarousel();
}
if (prevBtn) {
prevBtn.addEventListener('click', () => rotateCarousel(-1));
}
if (nextBtn) {
nextBtn.addEventListener('click', () => rotateCarousel(1));
}
items.forEach((item, index) => {
item.addEventListener('click', () => {
if (isAnimating) return;
currentIndex = index;
updateCarousel();
});
});
let touchStartX = 0;
let touchEndX = 0;
if (carousel) {
carousel.addEventListener('touchstart', (e) => {
touchStartX = e.changedTouches[0].screenX;
}, { passive: true });
carousel.addEventListener('touchend', (e) => {
touchEndX = e.changedTouches[0].screenX;
handleSwipe();
}, { passive: true });
}
function handleSwipe() {
if (isAnimating) return;
const swipeThreshold = 50;
const diff = touchStartX - touchEndX;
if (Math.abs(diff) > swipeThreshold) {
if (diff > 0) {
rotateCarousel(1);
} else {
rotateCarousel(-1);
}
}
}
if (carousel) {
carousel.addEventListener('wheel', (e) => {
if (isAnimating) return;
e.preventDefault();
if (e.deltaY > 0) {
rotateCarousel(1);
} else {
rotateCarousel(-1);
}
}, { passive: false });
}
updateCarousel();
}
function setupGameCards() {
const gameCards = document.querySelectorAll('.game-card');
gameCards.forEach(card => {
card.addEventListener('click', (event) => {
const isExpanded = card.classList.contains('expanded');
if (isExpanded) {
card.classList.remove('expanded');
} else {
gameCards.forEach(c => c.classList.remove('expanded'));
card.classList.add('expanded');
}
event.stopPropagation();
});
const playButton = card.querySelector('.play-button');
if (playButton) {
playButton.addEventListener('click', (event) => {
event.stopPropagation();
});
}
});
}