Files
aitbc/website/assets/js/main.js

270 lines
8.7 KiB
JavaScript

// Smooth scrolling for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId && targetId !== '#') {
const targetElement = document.querySelector(targetId);
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth'
});
}
}
});
});
// Add animation on scroll
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe all feature cards
document.querySelectorAll('.feature-card, .arch-component').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
// Dark mode functionality with enhanced persistence and system preference detection
}
}
}
// 2. Check system preference
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
return 'dark';
}
// 3. Default to dark (AITBC brand preference)
return 'dark';
}
function initializeTheme() {
const theme = getPreferredTheme();
setTheme(theme);
// Listen for system preference changes
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
// Only auto-switch if user hasn't manually set a preference
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light');
}
});
}
}
// Initialize theme immediately (before DOM loads)
// Touch gesture support for mobile navigation
class TouchNavigation {
constructor() {
this.touchStartX = 0;
this.touchStartY = 0;
this.touchEndX = 0;
this.touchEndY = 0;
this.minSwipeDistance = 50;
this.maxVerticalDistance = 100;
// Get all major sections for navigation
this.sections = ['hero', 'features', 'architecture', 'achievements', 'documentation'];
this.currentSectionIndex = 0;
this.bindEvents();
this.setupMobileOptimizations();
}
bindEvents() {
document.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: false });
document.addEventListener('touchmove', this.handleTouchMove.bind(this), { passive: false });
document.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: false });
}
handleTouchStart(e) {
this.touchStartX = e.touches[0].clientX;
this.touchStartY = e.touches[0].clientY;
}
handleTouchMove(e) {
// Prevent scrolling when detecting horizontal swipes
const touchCurrentX = e.touches[0].clientX;
const touchCurrentY = e.touches[0].clientY;
const deltaX = Math.abs(touchCurrentX - this.touchStartX);
const deltaY = Math.abs(touchCurrentY - this.touchStartY);
// If horizontal movement is greater than vertical, prevent default scrolling
if (deltaX > deltaY && deltaX > 10) {
e.preventDefault();
}
}
handleTouchEnd(e) {
this.touchEndX = e.changedTouches[0].clientX;
this.touchEndY = e.changedTouches[0].clientY;
const deltaX = this.touchEndX - this.touchStartX;
const deltaY = Math.abs(this.touchEndY - this.touchStartY);
// Only process swipe if vertical movement is minimal
if (deltaY < this.maxVerticalDistance && Math.abs(deltaX) > this.minSwipeDistance) {
if (deltaX > 0) {
this.swipeRight();
} else {
this.swipeLeft();
}
}
}
swipeLeft() {
// Navigate to next section
const nextIndex = Math.min(this.currentSectionIndex + 1, this.sections.length - 1);
this.navigateToSection(nextIndex);
}
swipeRight() {
// Navigate to previous section
const prevIndex = Math.max(this.currentSectionIndex - 1, 0);
this.navigateToSection(prevIndex);
}
navigateToSection(index) {
const sectionId = this.sections[index];
const element = document.getElementById(sectionId);
if (element) {
this.currentSectionIndex = index;
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Update URL hash without triggering scroll
history.replaceState(null, null, `#${sectionId}`);
}
}
setupMobileOptimizations() {
// Add touch-friendly interactions
this.setupTouchButtons();
this.setupScrollOptimizations();
this.setupMobileMenu();
}
setupTouchButtons() {
// Make buttons more touch-friendly
const buttons = document.querySelectorAll('button, .cta-button, .nav-button');
buttons.forEach(button => {
button.addEventListener('touchstart', () => {
button.style.transform = 'scale(0.98)';
}, { passive: true });
button.addEventListener('touchend', () => {
button.style.transform = '';
}, { passive: true });
});
}
setupScrollOptimizations() {
// Improve momentum scrolling on iOS
if ('webkitOverflowScrolling' in document.body.style) {
document.body.style.webkitOverflowScrolling = 'touch';
}
// Add smooth scrolling for anchor links with touch feedback
document.querySelectorAll('a[href^="#"]').forEach(link => {
link.addEventListener('touchstart', () => {
link.style.opacity = '0.7';
}, { passive: true });
link.addEventListener('touchend', () => {
link.style.opacity = '';
}, { passive: true });
});
}
setupMobileMenu() {
// Create mobile menu toggle if nav is hidden on mobile
const nav = document.querySelector('nav');
if (nav && window.innerWidth < 768) {
this.createMobileMenu();
}
}
createMobileMenu() {
// Create hamburger menu for mobile
const header = document.querySelector('header');
if (!header) return;
const mobileMenuBtn = document.createElement('button');
mobileMenuBtn.className = 'mobile-menu-btn';
mobileMenuBtn.innerHTML = '☰';
mobileMenuBtn.setAttribute('aria-label', 'Toggle mobile menu');
const nav = document.querySelector('nav');
if (nav) {
nav.style.display = 'none';
mobileMenuBtn.addEventListener('click', () => {
const isOpen = nav.style.display !== 'none';
nav.style.display = isOpen ? 'none' : 'flex';
mobileMenuBtn.innerHTML = isOpen ? '☰' : '✕';
});
// Close menu when clicking outside
document.addEventListener('click', (e) => {
if (!header.contains(e.target)) {
nav.style.display = 'none';
mobileMenuBtn.innerHTML = '☰';
}
});
header.appendChild(mobileMenuBtn);
}
}
updateCurrentSection() {
// Update current section based on scroll position
const scrollY = window.scrollY + window.innerHeight / 2;
this.sections.forEach((sectionId, index) => {
const element = document.getElementById(sectionId);
if (element) {
const rect = element.getBoundingClientRect();
const elementTop = rect.top + window.scrollY;
const elementBottom = elementTop + rect.height;
if (scrollY >= elementTop && scrollY < elementBottom) {
this.currentSectionIndex = index;
}
}
});
}
}
// Initialize touch navigation when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
new TouchNavigation();
// Update current section on scroll
window.addEventListener('scroll', () => {
if (window.touchNav) {
window.touchNav.updateCurrentSection();
}
}, { passive: true });
});