200 lines
6.5 KiB
JavaScript
200 lines
6.5 KiB
JavaScript
// js for Lumeex
|
|
// https://git.djeex.fr/Djeex/lumeex
|
|
|
|
// Fade in effect for elements with class 'appear'
|
|
const setupIntersectionObserver = () => {
|
|
document.querySelectorAll('.appear').forEach(parent => {
|
|
const children = parent.querySelectorAll('.appear');
|
|
children.forEach((child, i) => {
|
|
child.style.transitionDelay = `${i * 0.2}s`;
|
|
});
|
|
});
|
|
|
|
const items = document.querySelectorAll('.appear');
|
|
const io = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
entry.target.classList.toggle('inview', entry.isIntersecting);
|
|
});
|
|
});
|
|
items.forEach((item) => io.observe(item));
|
|
};
|
|
|
|
// Loader fade out after page load
|
|
const setupLoader = () => {
|
|
window.addEventListener('load', () => {
|
|
setTimeout(() => {
|
|
const loader = document.querySelector('.page-loader');
|
|
if (loader) loader.classList.add('hidden');
|
|
}, 50);
|
|
});
|
|
};
|
|
|
|
// Hero background randomizer
|
|
const randomizeHeroBackground = () => {
|
|
const heroBg = document.querySelector(".hero-background");
|
|
if (!heroBg) return;
|
|
fetch("/data/gallery.json")
|
|
.then((res) => res.json())
|
|
.then((images) => {
|
|
if (images.length === 0) return;
|
|
let currentIndex = Math.floor(Math.random() * images.length);
|
|
heroBg.style.backgroundImage = `url(/img/${images[currentIndex]})`;
|
|
if (images.length < 2) return; // <-- Prevent interval if only one image
|
|
setInterval(() => {
|
|
let nextIndex;
|
|
do {
|
|
nextIndex = Math.floor(Math.random() * images.length);
|
|
} while (nextIndex === currentIndex);
|
|
const nextImage = images[nextIndex];
|
|
heroBg.style.setProperty("--next-image", `url(/img/${nextImage})`);
|
|
heroBg.classList.add("fade-in");
|
|
const onTransitionEnd = () => {
|
|
heroBg.style.backgroundImage = `url(/img/${nextImage})`;
|
|
heroBg.classList.remove("fade-in");
|
|
heroBg.removeEventListener("transitionend", onTransitionEnd);
|
|
};
|
|
heroBg.addEventListener("transitionend", onTransitionEnd);
|
|
currentIndex = nextIndex;
|
|
}, 7000);
|
|
})
|
|
.catch(console.error);
|
|
};
|
|
|
|
// Gallery randomizer to shuffle gallery sections on page load
|
|
const shuffleGallery = () => {
|
|
const gallery = document.querySelector('.gallery');
|
|
if (!gallery) return;
|
|
const sections = Array.from(gallery.querySelectorAll('.section'));
|
|
while (sections.length) {
|
|
const randomIndex = Math.floor(Math.random() * sections.length);
|
|
gallery.appendChild(sections.splice(randomIndex, 1)[0]);
|
|
}
|
|
};
|
|
|
|
// Tags filter functionality
|
|
const setupTagFilter = () => {
|
|
const galleryContainer = document.querySelector('#gallery');
|
|
const allSections = document.querySelectorAll('.section[data-tags]');
|
|
const allTags = document.querySelectorAll('.tag');
|
|
let activeTags = [];
|
|
let lastClickedTag = null; // remembers the last clicked tag
|
|
let lastClickedSection = null; // remembers the last clicked section (photo)
|
|
|
|
const applyFilter = () => {
|
|
let filteredSections = [];
|
|
let matchingSection = null;
|
|
|
|
allSections.forEach((section) => {
|
|
const sectionTags = section.dataset.tags.toLowerCase().split(/\s+/);
|
|
const hasAllTags = activeTags.every((tag) => sectionTags.includes(tag));
|
|
section.style.display = hasAllTags ? '' : 'none';
|
|
|
|
if (hasAllTags) {
|
|
if (lastClickedSection === section) {
|
|
matchingSection = section;
|
|
} else {
|
|
filteredSections.push(section);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Remove all filtered sections from DOM before reordering
|
|
if (galleryContainer) {
|
|
[matchingSection, ...filteredSections].forEach(section => {
|
|
if (section && galleryContainer.contains(section)) {
|
|
galleryContainer.removeChild(section);
|
|
}
|
|
});
|
|
if (matchingSection) {
|
|
galleryContainer.prepend(matchingSection);
|
|
}
|
|
filteredSections.forEach(section => {
|
|
galleryContainer.appendChild(section);
|
|
});
|
|
}
|
|
|
|
// Update tag styles
|
|
allTags.forEach((tagEl) => {
|
|
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
|
|
tagEl.classList.toggle('active', activeTags.includes(tagText));
|
|
});
|
|
|
|
// Update the URL
|
|
const base = window.location.pathname;
|
|
const query = activeTags.length > 0 ? `?tag=${activeTags.join(',')}` : '';
|
|
window.history.pushState({}, '', base + query);
|
|
|
|
// Scroll to the gallery
|
|
if (galleryContainer) {
|
|
galleryContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
};
|
|
|
|
allTags.forEach((tagEl) => {
|
|
tagEl.addEventListener('click', () => {
|
|
const tagText = tagEl.textContent.replace('#', '').toLowerCase();
|
|
lastClickedTag = tagText; // remembers the last clicked tag
|
|
lastClickedSection = tagEl.closest('.section'); // remembers the last clicked section
|
|
|
|
if (activeTags.includes(tagText)) {
|
|
activeTags = activeTags.filter((t) => t !== tagText);
|
|
} else {
|
|
activeTags.push(tagText);
|
|
}
|
|
applyFilter();
|
|
});
|
|
});
|
|
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const urlTags = params.get('tag');
|
|
if (urlTags) {
|
|
activeTags = urlTags.split(',').map((t) => t.toLowerCase());
|
|
lastClickedTag = activeTags[activeTags.length - 1] || null;
|
|
lastClickedSection = null; // No section selected from URL
|
|
applyFilter();
|
|
}
|
|
});
|
|
};
|
|
|
|
// Disable right click and drag
|
|
const disableRightClickAndDrag = () => {
|
|
document.addEventListener('contextmenu', (e) => e.preventDefault());
|
|
document.addEventListener('dragstart', (e) => e.preventDefault());
|
|
};
|
|
|
|
// Scroll-to-top button functionality
|
|
const setupScrollToTopButton = () => {
|
|
const scrollBtn = document.getElementById("scrollToTop");
|
|
window.addEventListener("scroll", () => {
|
|
scrollBtn.style.display = window.scrollY > 300 ? "block" : "none";
|
|
});
|
|
scrollBtn.addEventListener("click", () => {
|
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
});
|
|
};
|
|
|
|
// Adjust navigation list items
|
|
const fixNavSeparators = () => {
|
|
const items = document.querySelectorAll('.nav-list li');
|
|
let prevTop = null;
|
|
items.forEach((item) => {
|
|
const top = item.getBoundingClientRect().top;
|
|
item.classList.toggle('first-on-line', prevTop !== null && top !== prevTop);
|
|
prevTop = top;
|
|
});
|
|
};
|
|
|
|
// Initialize all functions
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
setupIntersectionObserver();
|
|
setupLoader();
|
|
shuffleGallery();
|
|
randomizeHeroBackground();
|
|
setupTagFilter();
|
|
disableRightClickAndDrag();
|
|
setupScrollToTopButton();
|
|
fixNavSeparators();
|
|
});
|
|
|
|
window.addEventListener('resize', fixNavSeparators); |