Files
o-k-i.net/src/assets/js/main.js
T

231 lines
7.4 KiB
JavaScript
Raw Normal View History

2025-11-05 17:32:17 +04:00
// Smooth scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// Mobile menu toggle
const menuToggle = document.querySelector('.menu-toggle');
const navLinks = document.querySelector('.nav-links');
let menuOpen = false;
menuToggle?.addEventListener('click', () => {
menuOpen = !menuOpen;
if (menuOpen) {
navLinks.style.display = 'flex';
navLinks.style.position = 'absolute';
navLinks.style.top = '100%';
navLinks.style.left = '0';
navLinks.style.right = '0';
navLinks.style.background = 'rgba(0, 0, 0, 0.98)';
navLinks.style.flexDirection = 'column';
navLinks.style.padding = '1rem';
navLinks.style.borderTop = '2px solid #E8A625';
// Animate menu toggle
menuToggle.children[0].style.transform = 'rotate(45deg) translateY(8px)';
menuToggle.children[1].style.opacity = '0';
menuToggle.children[2].style.transform = 'rotate(-45deg) translateY(-8px)';
} else {
navLinks.style.display = 'none';
// Reset menu toggle
menuToggle.children[0].style.transform = '';
menuToggle.children[1].style.opacity = '1';
menuToggle.children[2].style.transform = '';
}
});
// Close mobile menu when clicking outside
document.addEventListener('click', (e) => {
if (menuOpen && !menuToggle.contains(e.target) && !navLinks.contains(e.target)) {
menuOpen = false;
navLinks.style.display = 'none';
menuToggle.children[0].style.transform = '';
menuToggle.children[1].style.opacity = '1';
menuToggle.children[2].style.transform = '';
}
});
// Scroll animations
const observerOptions = {
2025-11-05 17:56:25 +04:00
threshold: 0.15,
rootMargin: '0px 0px -50px 0px'
2025-11-05 17:32:17 +04:00
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
2025-11-05 17:56:25 +04:00
entry.target.classList.add('animate-in');
2025-11-05 17:32:17 +04:00
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe all cards
2025-11-05 17:56:25 +04:00
document.querySelectorAll('.service-card, .project-card, .value-card, .partner-card').forEach(el => {
el.classList.add('fade-in-element');
2025-11-05 17:32:17 +04:00
observer.observe(el);
});
// Navbar scroll effect
let lastScroll = 0;
const nav = document.querySelector('nav');
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset;
if (currentScroll > 100) {
nav.style.background = 'rgba(0, 0, 0, 0.98)';
nav.style.boxShadow = '0 2px 20px rgba(232, 166, 37, 0.3)';
} else {
nav.style.background = 'rgba(0, 0, 0, 0.95)';
nav.style.boxShadow = '';
}
// Hide/show navbar on scroll
if (currentScroll > lastScroll && currentScroll > 500) {
nav.style.transform = 'translateY(-100%)';
} else {
nav.style.transform = 'translateY(0)';
}
lastScroll = currentScroll;
});
// Network animation enhancement
function createNetworkLine() {
const nodes = document.querySelectorAll('.node');
const networkBg = document.querySelector('.network-bg');
if (nodes.length > 1 && networkBg) {
for (let i = 0; i < nodes.length - 1; i++) {
const line = document.createElement('div');
line.style.position = 'absolute';
line.style.height = '1px';
line.style.background = 'linear-gradient(90deg, transparent, #E8A625, transparent)';
line.style.opacity = '0.2';
line.style.animation = `pulse 4s infinite ${i * 0.5}s`;
const node1 = nodes[i];
const node2 = nodes[i + 1];
// Calculate line position and angle
const x1 = parseFloat(node1.style.left);
const y1 = parseFloat(node1.style.top);
const x2 = parseFloat(node2.style.left);
const y2 = parseFloat(node2.style.top);
const distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
line.style.width = distance + '%';
line.style.left = x1 + '%';
line.style.top = y1 + '%';
line.style.transform = `rotate(${angle}deg)`;
line.style.transformOrigin = '0 0';
networkBg.appendChild(line);
}
}
}
// Initialize network lines
createNetworkLine();
// Dynamic typing effect for hero subtitle
const subtitle = document.querySelector('.hero-subtitle');
if (subtitle) {
const text = subtitle.textContent;
subtitle.textContent = '';
let index = 0;
function typeWriter() {
if (index < text.length) {
subtitle.textContent += text.charAt(index);
index++;
setTimeout(typeWriter, 100);
}
}
setTimeout(typeWriter, 800);
}
// Add hover effect to project cards with tilt
document.querySelectorAll('.project-card, .service-card').forEach(card => {
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const rotateX = (y - centerY) / 20;
const rotateY = (centerX - x) / 20;
card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateY(-5px)`;
});
card.addEventListener('mouseleave', () => {
card.style.transform = '';
});
});
// Easter egg: Konami code
const konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
let konamiIndex = 0;
document.addEventListener('keydown', (e) => {
if (e.key === konamiCode[konamiIndex]) {
konamiIndex++;
if (konamiIndex === konamiCode.length) {
document.body.style.animation = 'pulse 1s';
setTimeout(() => {
alert('🎮 Félicitations! Vous avez trouvé le code secret! Bienvenue dans la résistance numérique!');
document.body.style.animation = '';
}, 1000);
konamiIndex = 0;
}
} else {
konamiIndex = 0;
}
});
// Progressive enhancement: Add loading states
// Disabled - not needed for external links
// document.querySelectorAll('.service-link, .project-card').forEach(link => {
// link.addEventListener('click', function(e) {
// if (this.href && this.href !== '#') {
// this.style.opacity = '0.6';
// this.innerHTML += ' ⌛';
// }
// });
// });
// Custom donation handler
function handleCustomDonation(type) {
const input = document.getElementById(type === 'oneTime' ? 'customAmountOneTime' : 'customAmountMonthly');
const amount = input.value;
if (!amount || amount < 1) {
alert('Veuillez entrer un montant valide');
return;
}
// Redirect to custom amount Stripe URL
const stripeUrl = type === 'oneTime'
? 'https://don.o-k-i.net/b/6oE2aO94P88X9u8eV4' // Custom one-time donation
: 'https://don.o-k-i.net/b/6oE2aO94P88X9u8eV4'; // Uses same link for custom amounts
window.location.href = stripeUrl;
}