fix performance and new mobile menu

Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
rodrigo committed 2026-02-22 16:46:43 -03:00
1 parent 989d7f0b98
commit 6904171303
4 files changed
+436 -142

No files matched your search

+145 -40
View File
@@ -33,8 +33,11 @@
cookieAccept: document.getElementById('cookie-accept'),
cookieDecline: document.getElementById('cookie-decline'),
themeToggle: document.getElementById('theme-toggle'),
themeToggleMobile: document.getElementById('theme-toggle-mobile'),
mobileMenuToggle: document.getElementById('mobile-menu-toggle'),
mainNav: document.getElementById('main-nav'),
mobileMenuClose: document.getElementById('mobile-menu-close'),
mobileNav: document.getElementById('mobile-nav'),
mobileMenuOverlay: document.getElementById('mobile-menu-overlay'),
header: document.querySelector('.site-header'),
loadMoreBtn: document.getElementById('load-more-posts'),
postsContainer: document.getElementById('posts-container'),
@@ -72,25 +75,31 @@
// ============= MODO ESCURO =============
function initDarkMode() {
if (!elements.themeToggle) return;
const toggles = [elements.themeToggle, elements.themeToggleMobile].filter(Boolean);
if (toggles.length === 0) return;
const icon = elements.themeToggle.querySelector('i');
const currentTheme = localStorage.getItem('theme');
// Aplicar tema inicial
if (currentTheme === 'light') {
body.classList.remove('dark-mode');
icon?.classList.replace('fa-sun', 'fa-moon');
toggles.forEach(toggle => {
const icon = toggle.querySelector('i');
icon?.classList.replace('fa-sun', 'fa-moon');
});
} else {
body.classList.add('dark-mode');
icon?.classList.replace('fa-moon', 'fa-sun');
toggles.forEach(toggle => {
const icon = toggle.querySelector('i');
icon?.classList.replace('fa-moon', 'fa-sun');
});
if (!currentTheme) {
localStorage.setItem('theme', 'dark');
}
}
// Toggle modo escuro
elements.themeToggle.addEventListener('click', (e) => {
function toggleDarkMode(e) {
e.preventDefault();
e.stopPropagation();
@@ -99,50 +108,94 @@
localStorage.setItem('theme', isDark ? 'dark' : 'light');
if (icon) {
icon.classList.replace(isDark ? 'fa-moon' : 'fa-sun', isDark ? 'fa-sun' : 'fa-moon');
}
toggles.forEach(toggle => {
const icon = toggle.querySelector('i');
if (icon) {
icon.classList.replace(isDark ? 'fa-moon' : 'fa-sun', isDark ? 'fa-sun' : 'fa-moon');
}
});
}
toggles.forEach(toggle => {
toggle.addEventListener('click', toggleDarkMode);
});
}
// ============= MENU MOBILE =============
function initMobileMenu() {
if (!elements.mobileMenuToggle || !elements.mainNav) return;
function toggleMenu(shouldOpen) {
const isOpen = shouldOpen ?? !elements.mobileMenuToggle.classList.contains('active');
elements.mobileMenuToggle.classList.toggle('active', isOpen);
elements.mobileMenuToggle.setAttribute('aria-expanded', isOpen);
elements.mainNav.classList.toggle('active', isOpen);
body.classList.toggle('menu-open', isOpen);
body.style.overflow = isOpen ? 'hidden' : '';
if (!elements.mobileMenuToggle || !elements.mobileNav || !elements.mobileMenuOverlay) {
return;
}
// Toggle menu
function openMenu() {
elements.mobileMenuToggle.classList.add('active');
elements.mobileMenuToggle.setAttribute('aria-expanded', 'true');
elements.mobileNav.classList.add('active');
elements.mobileMenuOverlay.classList.add('active');
body.classList.add('menu-open');
}
function closeMenu() {
elements.mobileMenuToggle.classList.remove('active');
elements.mobileMenuToggle.setAttribute('aria-expanded', 'false');
elements.mobileNav.classList.remove('active');
elements.mobileMenuOverlay.classList.remove('active');
body.classList.remove('menu-open');
}
function toggleMenu() {
if (elements.mobileNav.classList.contains('active')) {
closeMenu();
} else {
openMenu();
}
}
// Abrir/fechar menu ao clicar no botão hamburguer
elements.mobileMenuToggle.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
toggleMenu();
});
// Fechar menu ao clicar em links
elements.mainNav.addEventListener('click', (e) => {
if (e.target.tagName === 'A' && !e.target.closest('.theme-toggle-item')) {
toggleMenu(false);
}
// Fechar menu ao clicar no botão X
if (elements.mobileMenuClose) {
elements.mobileMenuClose.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
closeMenu();
});
}
// Fechar menu ao clicar no overlay
elements.mobileMenuOverlay.addEventListener('click', (e) => {
e.preventDefault();
closeMenu();
});
// Fechar menu ao clicar fora
document.addEventListener('click', (e) => {
if (body.classList.contains('menu-open')) {
const isClickInsideNav = elements.mainNav.contains(e.target);
const isClickOnToggle = elements.mobileMenuToggle.contains(e.target);
if (!isClickInsideNav && !isClickOnToggle) {
toggleMenu(false);
}
// Fechar menu ao clicar em links
const mobileLinks = elements.mobileNav.querySelectorAll('a');
mobileLinks.forEach(link => {
link.addEventListener('click', (e) => {
// Não previne o comportamento padrão para permitir navegação
// Apenas fecha o menu
closeMenu();
});
});
// Fechar menu ao clicar no botão de tema mobile
if (elements.themeToggleMobile) {
elements.themeToggleMobile.addEventListener('click', (e) => {
// Não fecha imediatamente, só após trocar tema
// O evento de tema já está tratado em initDarkMode
});
}
// Fechar menu com tecla ESC
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && elements.mobileNav.classList.contains('active')) {
closeMenu();
}
});
}
@@ -177,12 +230,25 @@
function initHeaderScroll() {
if (!elements.header) return;
const handleScroll = debounce(() => {
const shouldElevate = window.scrollY > 100;
let lastScrollY = window.scrollY;
let ticking = false;
const updateHeader = () => {
const shouldElevate = lastScrollY > 100;
elements.header.style.boxShadow = shouldElevate
? '0 4px 20px rgba(0,0,0,0.15)'
: '0 2px 10px rgba(0,0,0,0.1)';
}, 50);
ticking = false;
};
const handleScroll = () => {
lastScrollY = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(updateHeader);
ticking = true;
}
};
window.addEventListener('scroll', handleScroll, { passive: true });
}
@@ -198,8 +264,13 @@
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
// Usar requestAnimationFrame para animação suave
requestAnimationFrame(() => {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
});
// Parar de observar após animar
observer.unobserve(entry.target);
}
});
}, observerOptions);
@@ -208,7 +279,7 @@
cards.forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'all 0.6s ease';
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(card);
});
}
@@ -301,6 +372,39 @@
// ============= INICIALIZAÇÃO =============
/**
* Lazy loading de imagens nativas
*/
function initLazyLoading() {
// Verificar se o navegador suporta lazy loading nativo
if ('loading' in HTMLImageElement.prototype) {
const images = document.querySelectorAll('img[loading="lazy"]');
images.forEach(img => {
if (img.dataset.src) {
img.src = img.dataset.src;
}
});
} else {
// Fallback: usar Intersection Observer
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
}
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[loading="lazy"]').forEach(img => {
imageObserver.observe(img);
});
}
}
document.addEventListener('DOMContentLoaded', () => {
initCookieBanner();
initDarkMode();
@@ -310,6 +414,7 @@
initCardAnimations();
initRippleEffect();
initLoadMorePosts();
initLazyLoading();
});
})();