|
| 1 | +// Initialize AOS (Animate On Scroll) |
| 2 | +AOS.init({ once: true, offset: 50, duration: 800 }); |
| 3 | + |
| 4 | +// Mobile Menu Toggle |
| 5 | +document.getElementById('mobile-btn').addEventListener('click', () => { |
| 6 | + document.getElementById('mobile-menu').classList.toggle('hidden'); |
| 7 | +}); |
| 8 | + |
| 9 | +// Filter Projects |
| 10 | +function filterProjects(cat, btn) { |
| 11 | + document.querySelectorAll('.filter-btn').forEach(b => { |
| 12 | + b.classList.remove('bg-teal-500', 'text-white', 'border-teal-500'); |
| 13 | + b.classList.add('border-slate-700', 'text-slate-400'); |
| 14 | + }); |
| 15 | + btn.classList.remove('border-slate-700', 'text-slate-400'); |
| 16 | + btn.classList.add('bg-teal-500', 'text-white', 'border-teal-500'); |
| 17 | + document.querySelectorAll('.project-card').forEach(card => { |
| 18 | + if (cat === 'all' || card.classList.contains(cat)) { |
| 19 | + card.style.display = 'block'; |
| 20 | + card.classList.add('aos-animate'); |
| 21 | + } else { |
| 22 | + card.style.display = 'none'; |
| 23 | + } |
| 24 | + }); |
| 25 | +} |
| 26 | + |
| 27 | +// --- 4-WAY BUBBLE GENERATOR --- |
| 28 | +function createBubbles() { |
| 29 | + const container = document.getElementById('four-way-bg'); |
| 30 | + const icons = ['fa-bug', 'fa-bug', 'fa-server', 'fa-docker', 'fa-check-circle', 'fa-code-branch']; |
| 31 | + const animations = ['move-up', 'move-down', 'move-left', 'move-right']; |
| 32 | + const colors = ['p-bug', 'p-dev', 'p-qa']; |
| 33 | + |
| 34 | + for (let i = 0; i < 25; i++) { |
| 35 | + const bubble = document.createElement('i'); |
| 36 | + const randomIcon = icons[Math.floor(Math.random() * icons.length)]; |
| 37 | + const randomAnim = animations[Math.floor(Math.random() * animations.length)]; |
| 38 | + const randomColor = colors[Math.floor(Math.random() * colors.length)]; |
| 39 | + |
| 40 | + bubble.classList.add('fas', randomIcon, 'bubble-particle', randomAnim, randomColor); |
| 41 | + |
| 42 | + // Random Positioning |
| 43 | + if (randomAnim === 'move-up' || randomAnim === 'move-down') { |
| 44 | + bubble.style.left = Math.random() * 90 + 5 + '%'; |
| 45 | + } else { |
| 46 | + bubble.style.top = Math.random() * 90 + 5 + '%'; |
| 47 | + } |
| 48 | + |
| 49 | + bubble.style.animationDuration = (Math.random() * 3 + 3) + 's'; |
| 50 | + bubble.style.animationDelay = (Math.random() * 5) + 's'; |
| 51 | + |
| 52 | + container.appendChild(bubble); |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +// Typing Effect |
| 57 | +const textElement = document.getElementById('typed-text'); |
| 58 | + |
| 59 | +const phrases = [ |
| 60 | + "DevOps Engineer", |
| 61 | + "DevOps Freelancer", |
| 62 | + "CI/CD Pipelines", |
| 63 | + "AWS & Docker" |
| 64 | +]; |
| 65 | + |
| 66 | +let phraseIndex = 0; |
| 67 | +let charIndex = 0; |
| 68 | +let isDeleting = false; |
| 69 | +let typeSpeed = 100; |
| 70 | + |
| 71 | +function typeText() { |
| 72 | + const currentPhrase = phrases[phraseIndex]; |
| 73 | + |
| 74 | + if (isDeleting) { |
| 75 | + textElement.textContent = currentPhrase.substring(0, charIndex - 1); |
| 76 | + charIndex--; |
| 77 | + typeSpeed = 50; |
| 78 | + } else { |
| 79 | + textElement.textContent = currentPhrase.substring(0, charIndex + 1); |
| 80 | + charIndex++; |
| 81 | + typeSpeed = 100; |
| 82 | + } |
| 83 | + |
| 84 | + if (!isDeleting && charIndex === currentPhrase.length) { |
| 85 | + isDeleting = true; |
| 86 | + typeSpeed = 1800; |
| 87 | + } else if (isDeleting && charIndex === 0) { |
| 88 | + isDeleting = false; |
| 89 | + phraseIndex = (phraseIndex + 1) % phrases.length; |
| 90 | + typeSpeed = 500; |
| 91 | + } |
| 92 | + |
| 93 | + setTimeout(typeText, typeSpeed); |
| 94 | +} |
| 95 | + |
| 96 | +// Initialize on DOM Load |
| 97 | +document.addEventListener('DOMContentLoaded', () => { |
| 98 | + createBubbles(); |
| 99 | + typeText(); |
| 100 | +}); |
0 commit comments