📂 FileMgr
📍
/
home
/
u865173473
/
domains
/
yelagiri.co.in
/
public_html
/
js
✏️ /
⬅ Kembali
document.addEventListener('DOMContentLoaded', () => { // ======================================================== // 1️⃣ Custom Cursor Logic // ======================================================== const cursorDot = document.getElementById('cursor-dot'); const cursorCircle = document.getElementById('cursor-circle'); // Only enable on desktop/fine pointer devices const isTouch = window.matchMedia("(pointer: coarse)").matches; if (cursorDot && cursorCircle && !isTouch) { let mouseX = 0; let mouseY = 0; let circleX = 0; let circleY = 0; let isHovering = false; // Initialize tracking document.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; // Instant movement for the dot // translate(-50%, -50%) centers the element on the cursor cursorDot.style.transform = `translate3d(${mouseX}px, ${mouseY}px, 0) translate(-50%, -50%)`; }); // Hover Effect using Event Delegation document.addEventListener('mouseover', (e) => { if (e.target.closest('a, button, .cursor-hover')) { isHovering = true; } else { isHovering = false; } }); document.addEventListener('mouseout', (e) => { // Basic check to reset if leaving the window or similar // The mouseover delegation handles switching between elements. // We just need to check if we moved to something not interactive. if (!e.target.closest('a, button, .cursor-hover')) { isHovering = false; } }); // Smooth Animation Loop function animateCursor() { // Lerp factor for smooth delay (0.15 is snappy but smooth) const lerp = 0.15; circleX += (mouseX - circleX) * lerp; circleY += (mouseY - circleY) * lerp; // Apply transform to circle const scale = isHovering ? 1.5 : 1; cursorCircle.style.transform = `translate3d(${circleX}px, ${circleY}px, 0) translate(-50%, -50%) scale(${scale})`; requestAnimationFrame(animateCursor); } requestAnimationFrame(animateCursor); } else { // Ensure cursor elements are hidden on touch devices if (cursorDot) cursorDot.style.display = 'none'; if (cursorCircle) cursorCircle.style.display = 'none'; } // ======================================================== // 2️⃣ Navbar Scroll Background Issue // ======================================================== const navbar = document.getElementById('navbar'); if (navbar) { const handleScroll = () => { if (window.scrollY >= 80) { // Scrolled state: Dark, blurred, shadow, compact navbar.classList.add('bg-brand-dark/90', 'backdrop-blur-md', 'shadow-md'); navbar.classList.remove('py-6'); navbar.classList.add('py-4'); } else { // Top state: Transparent, spacious navbar.classList.remove('bg-brand-dark/90', 'backdrop-blur-md', 'shadow-md'); navbar.classList.remove('py-4'); navbar.classList.add('py-6'); } }; window.addEventListener('scroll', handleScroll); // Trigger once on load to set initial state handleScroll(); } // ======================================================== // 3️⃣ Scroll Reveal Animation // ======================================================== const observerOptions = { root: null, rootMargin: '0px', threshold: 0.1 }; const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate-fade-in-up', 'opacity-100'); entry.target.classList.remove('opacity-0'); observer.unobserve(entry.target); } }); }, observerOptions); const revealElements = document.querySelectorAll('.reveal-on-scroll'); revealElements.forEach(el => { el.classList.add('opacity-0'); // Initial state observer.observe(el); }); // ======================================================== // 4️⃣ Statistic Counters // ======================================================== const counters = document.querySelectorAll('.counter'); const counterObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const counter = entry.target; const target = +counter.getAttribute('data-target'); const duration = 2000; // ms const increment = target / (duration / 16); // 60fps let current = 0; const updateCounter = () => { current += increment; if (current < target) { counter.innerText = Math.ceil(current); requestAnimationFrame(updateCounter); } else { counter.innerText = target; } }; updateCounter(); observer.unobserve(counter); } }); }, observerOptions); counters.forEach(counter => counterObserver.observe(counter)); // ======================================================== // 5️⃣ Testimonial Carousel // ======================================================== const slides = document.querySelectorAll('.testimonial-slide'); const prevBtn = document.getElementById('prev-btn'); const nextBtn = document.getElementById('next-btn'); let currentSlide = 0; let slideInterval; function showSlide(index) { slides.forEach((slide, i) => { if (i === index) { slide.classList.remove('opacity-0', 'pointer-events-none'); slide.classList.add('opacity-100'); } else { slide.classList.remove('opacity-100'); slide.classList.add('opacity-0', 'pointer-events-none'); } }); } function nextSlide() { currentSlide = (currentSlide + 1) % slides.length; showSlide(currentSlide); } function prevSlide() { currentSlide = (currentSlide - 1 + slides.length) % slides.length; showSlide(currentSlide); } if (slides.length > 0) { // Auto slide slideInterval = setInterval(nextSlide, 5000); // Manual controls if (nextBtn) { nextBtn.addEventListener('click', () => { clearInterval(slideInterval); nextSlide(); slideInterval = setInterval(nextSlide, 5000); }); } if (prevBtn) { prevBtn.addEventListener('click', () => { clearInterval(slideInterval); prevSlide(); slideInterval = setInterval(nextSlide, 5000); }); } } // ======================================================== // 6️⃣ Mobile Menu Toggle // ======================================================== const mobileBtn = document.getElementById('mobile-menu-btn'); const mobileMenu = document.getElementById('mobile-menu'); if (mobileBtn && mobileMenu) { mobileBtn.addEventListener('click', () => { // Check if menu is currently hidden (has translate-x-full) const isHidden = mobileMenu.classList.contains('translate-x-full'); if (isHidden) { // Open menu mobileMenu.classList.remove('translate-x-full'); mobileBtn.innerHTML = '<i class="fas fa-times"></i>'; // Animate links const links = mobileMenu.querySelectorAll('.mobile-link'); links.forEach((link, index) => { setTimeout(() => { link.classList.remove('translate-y-10', 'opacity-0'); }, 100 + (index * 50)); }); } else { // Close menu mobileMenu.classList.add('translate-x-full'); mobileBtn.innerHTML = '<i class="fas fa-bars"></i>'; // Reset links const links = mobileMenu.querySelectorAll('.mobile-link'); links.forEach(link => { link.classList.add('translate-y-10', 'opacity-0'); }); } }); // Close when clicking a link const mobileLinks = mobileMenu.querySelectorAll('a'); mobileLinks.forEach(link => { link.addEventListener('click', () => { mobileMenu.classList.add('translate-x-full'); mobileBtn.innerHTML = '<i class="fas fa-bars"></i>'; }); }); } }); /** * Global Modal Functions */ function openModal(modalId, subject = '') { const modal = document.getElementById(modalId); if (modal) { modal.classList.remove('hidden'); if (subject) { // Try to find subject input by ID first const subjectInput = document.getElementById('modalSubject'); if (subjectInput) { subjectInput.value = subject; } // Fallback: try by name const subjectInputByName = document.querySelector('input[name="subject"]'); if (subjectInputByName) { subjectInputByName.value = subject; } } } } function closeModal(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.add('hidden'); } }
💾 Simpan
Batal
⬅ public_html
1 item
Nama
Tipe
Ukuran
Diubah
Aksi
📜
main.js
js
10.2 KB
2026-01-25 15:45
✏️
👁️
🔤
🗑
✏️ Rename
Nama Baru
Simpan
Batal