Tenhal/static/js/main.js

214 lines
7.8 KiB
JavaScript

// --- Toggle Mobile Menu ---
// --- Mobile Navigation ---
function toggleMenu() {
document.getElementById('mobile-nav').classList.toggle('open');
}
// --- Intersection Observer for Scroll Animations ---
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) entry.target.classList.add('active');
});
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
// --- Generic Horizontal Scroll Logic (Services, Team, Testimonials) ---
function scrollCarousel(trackId, direction) {
const track = document.getElementById(trackId);
if (!track) return;
const firstItem = track.querySelector('.carousel-item');
if (!firstItem) return;
const itemWidth = firstItem.offsetWidth;
const gap = 24;
const scrollAmount = itemWidth + gap;
// Adjust direction for RTL
let scrollDir = direction;
if (document.dir === 'rtl') {
scrollDir = direction * -1;
}
if (scrollDir === -1) {
track.scrollBy({ left: -scrollAmount, behavior: 'smooth' });
} else {
track.scrollBy({ left: scrollAmount, behavior: 'smooth' });
}
}
// --- Product Slider Logic (Fade) ---
const slides = document.querySelectorAll('.product-slide');
const indicators = document.querySelectorAll('.indicator');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const productWrapper = document.querySelector('.product-slider-wrapper');
let currentSlide = 0;
const totalSlides = slides.length;
let slideInterval;
if(totalSlides > 0) {
function showSlide(index) {
if (index >= totalSlides) currentSlide = 0;
else if (index < 0) currentSlide = totalSlides - 1;
else currentSlide = index;
slides.forEach(slide => slide.classList.remove('active'));
indicators.forEach(ind => ind.classList.remove('active'));
slides[currentSlide].classList.add('active');
indicators[currentSlide].classList.add('active');
}
function nextSlide() {
showSlide(currentSlide + 1);
}
function prevSlide() {
showSlide(currentSlide - 1);
}
if(nextBtn) {
nextBtn.addEventListener('click', () => {
nextSlide();
resetTimer();
});
}
if(prevBtn) {
prevBtn.addEventListener('click', () => {
prevSlide();
resetTimer();
});
}
indicators.forEach((ind, index) => {
ind.addEventListener('click', () => {
showSlide(index);
resetTimer();
});
});
function startTimer() {
slideInterval = setInterval(nextSlide, 5000);
}
function resetTimer() {
clearInterval(slideInterval);
startTimer();
}
if(productWrapper) {
productWrapper.addEventListener('mouseenter', () => clearInterval(slideInterval));
productWrapper.addEventListener('mouseleave', startTimer);
startTimer();
}
}
// --- Form Submission (AJAX) ---
function showToast(message) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.style.transform = 'translateY(0)';
setTimeout(() => {
toast.style.transform = 'translateY(150%)';
}, 3000);
}
document.getElementById('contactForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
const submitBtn = this.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.textContent = '{% trans "Sending..." %}';
submitBtn.disabled = true;
fetch("{% url 'submit_inquiry' %}", {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if(data.status === 'success') {
showToast(data.message);
document.getElementById('contactForm').reset();
} else {
showToast('Error: ' + (data.message || 'Something went wrong'));
}
})
.catch(error => {
showToast('Error: Could not submit form.');
console.error('Error:', error);
})
.finally(() => {
submitBtn.textContent = originalText;
submitBtn.disabled = false;
});
});
// --- Marquee Width & Speed Fix ---
// Helper: Debounce function prevents the code from running too often
// (e.g. while scrolling on mobile which triggers resize events)
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function adjustMarqueeWidth() {
const track = document.getElementById('marquee-track');
if (!track) return;
// 1. Get the rendered width of the single list of partners
const currentWidth = track.scrollWidth;
// 2. Calculate how many times we need to repeat the list
// Target: We want the track to be at least 3x the screen width
// so it scrolls smoothly without running out of items before looping.
const targetWidth = window.innerWidth * 3;
const repeats = Math.max(2, Math.ceil(targetWidth / currentWidth));
// 3. Store the original HTML (the first run of the loop)
const originalContent = track.innerHTML;
// 4. Rebuild the track with the repeated content
track.innerHTML = '';
for (let i = 0; i < repeats; i++) {
track.innerHTML += originalContent;
}
// --- NEW: Calculate Constant Speed ---
// The animation moves -50% of the track width.
const totalWidth = track.scrollWidth;
const distanceToMove = totalWidth / 2;
// Desired speed in pixels per second (Adjust this number to make it faster/slower)
// 50 = Relaxed/Easy reading speed
// 100 = Faster
const pixelsPerSecond = 50;
// Duration = Distance / Speed
const durationInSeconds = distanceToMove / pixelsPerSecond;
// Apply the calculated duration
track.style.animationDuration = `${durationInSeconds}s`;
}
// Run on load
window.addEventListener('load', adjustMarqueeWidth);
// Run on resize (Debounced by 200ms to prevent flickering during scroll)
window.addEventListener('resize', debounce(adjustMarqueeWidth, 200));