371 lines
14 KiB
JavaScript
371 lines
14 KiB
JavaScript
/** @odoo-module **/
|
||
/**
|
||
* Aakriti Events – Premium Theme V2 JS
|
||
* Handles: sticky header, mobile drawer, gallery carousel, scroll reveal, counter animation
|
||
*/
|
||
|
||
(function () {
|
||
'use strict';
|
||
|
||
/* ── Helpers ──────────────────────────────────────────── */
|
||
function qs(sel, ctx) { return (ctx || document).querySelector(sel); }
|
||
function qsa(sel, ctx) { return (ctx || document).querySelectorAll(sel); }
|
||
function onReady(fn) {
|
||
if (document.readyState !== 'loading') { fn(); }
|
||
else { document.addEventListener('DOMContentLoaded', fn); }
|
||
}
|
||
|
||
/* ── Sticky Header ────────────────────────────────────── */
|
||
function initHeader() {
|
||
var header = qs('#v2-header');
|
||
if (!header) return;
|
||
|
||
function updateHeader() {
|
||
if (window.scrollY > 40) {
|
||
header.classList.add('scrolled');
|
||
} else {
|
||
header.classList.remove('scrolled');
|
||
}
|
||
}
|
||
|
||
window.addEventListener('scroll', updateHeader, { passive: true });
|
||
updateHeader();
|
||
}
|
||
|
||
/* ── Mobile Drawer ────────────────────────────────────── */
|
||
function initMobileDrawer() {
|
||
var toggle = qs('#v2-mobile-toggle');
|
||
var drawer = qs('#v2-mobile-drawer');
|
||
var overlay = qs('#v2-drawer-overlay');
|
||
var closeBtn = qs('#v2-drawer-close');
|
||
if (!toggle || !drawer) return;
|
||
|
||
function openDrawer() {
|
||
drawer.classList.add('open');
|
||
if (overlay) { overlay.classList.add('active'); }
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
function closeDrawer() {
|
||
drawer.classList.remove('open');
|
||
if (overlay) { overlay.classList.remove('active'); }
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
toggle.addEventListener('click', openDrawer);
|
||
if (closeBtn) { closeBtn.addEventListener('click', closeDrawer); }
|
||
if (overlay) { overlay.addEventListener('click', closeDrawer); }
|
||
|
||
qsa('#v2-mobile-drawer a').forEach(function (a) {
|
||
a.addEventListener('click', closeDrawer);
|
||
});
|
||
window.addEventListener('resize', function () {
|
||
if (window.innerWidth > 991) { closeDrawer(); }
|
||
});
|
||
}
|
||
|
||
/* ── Gallery Carousel ─────────────────────────────────── */
|
||
function initGallery() {
|
||
var track = qs('#v2-gallery-track');
|
||
var prevBtn = qs('#v2-gallery-prev');
|
||
var nextBtn = qs('#v2-gallery-next');
|
||
if (!track) return;
|
||
|
||
var current = 0;
|
||
var itemWidth = 0;
|
||
var visibleItems = 4;
|
||
|
||
function updateLayout() {
|
||
var ww = window.innerWidth;
|
||
if (ww > 991) {
|
||
visibleItems = 4;
|
||
} else if (ww > 576) {
|
||
visibleItems = 3;
|
||
} else {
|
||
visibleItems = 2;
|
||
}
|
||
|
||
var containerWidth = track.parentElement.offsetWidth;
|
||
var gap = 12; // White gap between slanted items in pixels
|
||
|
||
var items = qsa('.v2-gallery-item-slant', track);
|
||
|
||
// Calculate width 'w' so that 'visibleItems' fit exactly in containerWidth
|
||
var w = (containerWidth - (visibleItems - 1) * gap) / (0.75 * visibleItems + 0.25);
|
||
var mr = (-0.25 * w) + gap;
|
||
|
||
items.forEach(function (item, idx) {
|
||
item.style.width = w + 'px';
|
||
if (idx === items.length - 1) {
|
||
item.style.marginRight = '0px';
|
||
} else {
|
||
item.style.marginRight = mr + 'px';
|
||
}
|
||
});
|
||
|
||
itemWidth = (w * 0.75) + gap; // The effective step width to slide
|
||
|
||
current = clamp(current, 0, getMax());
|
||
goTo(current, false);
|
||
updateButtons();
|
||
}
|
||
|
||
function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); }
|
||
|
||
function getMax() {
|
||
var itemsCount = track.children.length;
|
||
return Math.max(0, itemsCount - visibleItems);
|
||
}
|
||
|
||
function updateButtons() {
|
||
if (prevBtn) {
|
||
if (current <= 0) {
|
||
prevBtn.style.opacity = '0.5';
|
||
prevBtn.style.pointerEvents = 'none';
|
||
} else {
|
||
prevBtn.style.opacity = '1';
|
||
prevBtn.style.pointerEvents = 'auto';
|
||
}
|
||
}
|
||
if (nextBtn) {
|
||
if (current >= getMax()) {
|
||
nextBtn.style.opacity = '0.5';
|
||
nextBtn.style.pointerEvents = 'none';
|
||
} else {
|
||
nextBtn.style.opacity = '1';
|
||
nextBtn.style.pointerEvents = 'auto';
|
||
}
|
||
}
|
||
}
|
||
|
||
function goTo(index, animate) {
|
||
if (animate === undefined) animate = true;
|
||
current = clamp(index, 0, getMax());
|
||
track.style.transition = animate ? 'transform 0.4s ease' : 'none';
|
||
track.style.transform = 'translateX(-' + (current * itemWidth) + 'px)';
|
||
updateButtons();
|
||
}
|
||
|
||
if (prevBtn) { prevBtn.addEventListener('click', function () { goTo(current - 1); resetAutoSlide(); }); }
|
||
if (nextBtn) { nextBtn.addEventListener('click', function () { goTo(current + 1); resetAutoSlide(); }); }
|
||
|
||
// Touch/swipe support
|
||
var startX = 0;
|
||
track.addEventListener('touchstart', function (e) { startX = e.touches[0].clientX; }, { passive: true });
|
||
track.addEventListener('touchend', function (e) {
|
||
var diff = startX - e.changedTouches[0].clientX;
|
||
if (Math.abs(diff) > 50) {
|
||
goTo(current + (diff > 0 ? 1 : -1));
|
||
resetAutoSlide();
|
||
}
|
||
}, { passive: true });
|
||
|
||
// Auto slide
|
||
var autoSlideInterval;
|
||
function startAutoSlide() {
|
||
autoSlideInterval = setInterval(function () {
|
||
if (current >= getMax()) {
|
||
goTo(0);
|
||
} else {
|
||
goTo(current + 1);
|
||
}
|
||
}, 3000);
|
||
}
|
||
function resetAutoSlide() {
|
||
clearInterval(autoSlideInterval);
|
||
startAutoSlide();
|
||
}
|
||
|
||
window.addEventListener('resize', updateLayout);
|
||
updateLayout();
|
||
startAutoSlide();
|
||
}
|
||
|
||
/* ── Scroll Reveal ────────────────────────────────────── */
|
||
function initReveal() {
|
||
var els = qsa('.v2-reveal');
|
||
if (!els.length) return;
|
||
|
||
if ('IntersectionObserver' in window) {
|
||
var io = new IntersectionObserver(function (entries) {
|
||
entries.forEach(function (entry) {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('visible');
|
||
io.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, { threshold: 0.12 });
|
||
els.forEach(function (el) { io.observe(el); });
|
||
} else {
|
||
els.forEach(function (el) { el.classList.add('visible'); });
|
||
}
|
||
}
|
||
|
||
/* ── Counter Animation ────────────────────────────────── */
|
||
function animateCounter(el, target, suffix) {
|
||
var start = 0;
|
||
var duration = 1800;
|
||
var startTime = null;
|
||
|
||
function step(ts) {
|
||
if (!startTime) startTime = ts;
|
||
var progress = Math.min((ts - startTime) / duration, 1);
|
||
var eased = 1 - Math.pow(1 - progress, 3); // ease out cubic
|
||
var val = Math.round(eased * target);
|
||
el.textContent = val + suffix;
|
||
if (progress < 1) { requestAnimationFrame(step); }
|
||
}
|
||
requestAnimationFrame(step);
|
||
}
|
||
|
||
function initCounters() {
|
||
var counters = qsa('[data-v2-counter]');
|
||
if (!counters.length) return;
|
||
|
||
var observed = false;
|
||
|
||
if ('IntersectionObserver' in window) {
|
||
var io = new IntersectionObserver(function (entries) {
|
||
entries.forEach(function (entry) {
|
||
if (entry.isIntersecting && !observed) {
|
||
observed = true;
|
||
counters.forEach(function (el) {
|
||
var target = parseInt(el.getAttribute('data-v2-counter'), 10);
|
||
var suffix = el.getAttribute('data-v2-suffix') || '';
|
||
animateCounter(el, target, suffix);
|
||
});
|
||
io.disconnect();
|
||
}
|
||
});
|
||
}, { threshold: 0.3 });
|
||
|
||
var statsSection = qs('#v2-stats');
|
||
if (statsSection) { io.observe(statsSection); }
|
||
} else {
|
||
counters.forEach(function (el) {
|
||
el.textContent = el.getAttribute('data-v2-counter') + (el.getAttribute('data-v2-suffix') || '');
|
||
});
|
||
}
|
||
}
|
||
|
||
/* ── Active nav link ──────────────────────────────────── */
|
||
function initActiveNav() {
|
||
var path = window.location.pathname;
|
||
qsa('.v2-nav a, .v2-drawer-nav a').forEach(function (a) {
|
||
var href = a.getAttribute('href') || '';
|
||
if (href === path || (href !== '/' && path.startsWith(href))) {
|
||
a.classList.add('active');
|
||
}
|
||
});
|
||
}
|
||
|
||
/* ── Service Tabs (Themes) ────────────────────────────── */
|
||
function initServiceTabs() {
|
||
var pillBtns = qsa('.svc-pill-btn');
|
||
var themePanels = qsa('.svc-fw-theme-panel');
|
||
if (!pillBtns.length || !themePanels.length) return;
|
||
|
||
pillBtns.forEach(function (btn) {
|
||
btn.addEventListener('click', function () {
|
||
var targetId = btn.getAttribute('data-target');
|
||
if (!targetId) return;
|
||
|
||
// Remove active class from all buttons and panels
|
||
pillBtns.forEach(function (b) { b.classList.remove('active'); });
|
||
themePanels.forEach(function (p) { p.classList.remove('active'); });
|
||
|
||
// Add active class to clicked button and target panel
|
||
btn.classList.add('active');
|
||
var targetPanel = qs('#' + targetId);
|
||
if (targetPanel) {
|
||
targetPanel.classList.add('active');
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/* ── Bootstrap ────────────────────────────────────────── */
|
||
function initPortfolio() {
|
||
const grid = document.getElementById('v2-portfolio-grid');
|
||
const tabs = document.querySelectorAll('.v2-portfolio-tab');
|
||
if (!grid || !tabs.length) return;
|
||
|
||
// Dynamic portfolio data
|
||
const portfolioData = [
|
||
{ category: 'weddings', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
|
||
{ category: 'corporate', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
|
||
{ category: 'birthday', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
|
||
{ category: 'cultural', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
|
||
{ category: 'private', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
|
||
{ category: 'weddings', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' }
|
||
];
|
||
|
||
// Function to render items
|
||
function renderItems(filter) {
|
||
grid.innerHTML = ''; // Clear current
|
||
|
||
portfolioData.forEach(item => {
|
||
if (filter === 'all' || filter === item.category) {
|
||
const itemHTML = `
|
||
<div class="v2-portfolio-item" data-category="${item.category}" style="opacity: 0; transform: scale(0.9); transition: opacity 0.4s ease, transform 0.4s ease;">
|
||
<div class="v2-portfolio-item-inner">
|
||
<img src="${item.img}" alt="${item.title}"/>
|
||
<div class="v2-portfolio-info">
|
||
<p style="text-align: center;">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
// Parse and append to trigger animation
|
||
const temp = document.createElement('div');
|
||
temp.innerHTML = itemHTML.trim();
|
||
const element = temp.firstChild;
|
||
grid.appendChild(element);
|
||
|
||
// Trigger animation after append
|
||
setTimeout(() => {
|
||
element.style.opacity = '1';
|
||
element.style.transform = 'scale(1)';
|
||
}, 50);
|
||
}
|
||
});
|
||
}
|
||
|
||
// Initial render
|
||
renderItems('all');
|
||
|
||
// Tab click handlers
|
||
tabs.forEach(tab => {
|
||
tab.addEventListener('click', function () {
|
||
tabs.forEach(t => t.classList.remove('active'));
|
||
this.classList.add('active');
|
||
|
||
const filter = this.getAttribute('data-filter');
|
||
|
||
// Fade out existing items
|
||
const currentItems = grid.querySelectorAll('.v2-portfolio-item');
|
||
currentItems.forEach(item => {
|
||
item.style.opacity = '0';
|
||
item.style.transform = 'scale(0.9)';
|
||
});
|
||
|
||
// Wait for fade out, then render new filtered list
|
||
setTimeout(() => {
|
||
renderItems(filter);
|
||
}, 400); // Wait for transition
|
||
});
|
||
});
|
||
}
|
||
|
||
onReady(function () {
|
||
initHeader();
|
||
initMobileDrawer();
|
||
initGallery();
|
||
initReveal();
|
||
initCounters();
|
||
initActiveNav();
|
||
initServiceTabs();
|
||
initPortfolio();
|
||
});
|
||
|
||
})();
|