/** * Yatra Premium Features - Modern SaaS UI Interactions * Advanced animations, micro-interactions, and scroll effects */ (function($) { 'use strict'; const YatraSaaS = { /** * Configuration */ config: { animationDuration: 600, scrollOffset: 50, intersectionThreshold: 0.1, parallaxIntensity: 0.3 }, /** * State management */ state: { isInitialized: false, observer: null, scrollY: 0, windowHeight: 0 }, /** * Initialize all SaaS functionality */ init() { if (this.state.isInitialized) return; this.cacheDom(); this.setupScrollAnimations(); this.bindEvents(); this.initParallaxEffects(); this.initCounterAnimations(); this.initCardInteractions(); this.initButtonEffects(); this.initKeyboardAccessibility(); this.state.isInitialized = true; this.log('🚀 Yatra SaaS UI initialized'); }, /** * Cache DOM elements */ cacheDom() { this.elements = { $window: $(window), $document: $(document), $body: $('body'), $saasPage: $('.yatra-saas-page'), $nav: $('.yatra-saas-nav'), $hero: $('.yatra-saas-hero'), $stats: $('.yatra-hero-stats .yatra-stat'), $featureCards: $('.yatra-feature-card'), $showcaseCards: $('.yatra-showcase-card'), $ctaButtons: $('.yatra-cta-primary, .yatra-cta-button'), $animatedElements: $('[data-aos]'), $parallaxElements: $('.yatra-hero-bg-pattern, .yatra-cta-bg-pattern') }; this.state.windowHeight = this.elements.$window.height(); }, /** * Setup scroll-based animations using Intersection Observer */ setupScrollAnimations() { if (!window.IntersectionObserver) return; this.state.observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { this.animateElement(entry.target); this.state.observer.unobserve(entry.target); } }); }, { threshold: this.config.intersectionThreshold, rootMargin: '50px 0px -50px 0px' }); // Observe elements with animation attributes this.elements.$animatedElements.each((index, element) => { this.state.observer.observe(element); }); }, /** * Animate individual element with stagger delay */ animateElement(element) { const $element = $(element); const delay = parseInt($element.attr('data-aos-delay') || 0); setTimeout(() => { $element.addClass('aos-animate'); // Trigger custom events for specific elements if ($element.hasClass('yatra-stat-number')) { this.animateCounter($element); } if ($element.hasClass('yatra-feature-card')) { this.animateFeatureCard($element); } }, delay); }, /** * Animate counter numbers with easing */ animateCounter($element) { const finalValue = $element.text(); const isPercentage = finalValue.includes('%'); const isDecimal = finalValue.includes('.'); const numericValue = parseFloat(finalValue.replace(/[^\d.]/g, '')); if (isNaN(numericValue)) return; let currentValue = 0; const increment = numericValue / 60; // 60 frames for smooth animation const duration = 2000; $element.text('0' + (isPercentage ? '%' : '')); const counter = setInterval(() => { currentValue += increment; if (currentValue >= numericValue) { currentValue = numericValue; clearInterval(counter); } let displayValue = Math.floor(currentValue); if (isDecimal) displayValue = currentValue.toFixed(1); $element.text(displayValue + (isPercentage ? '%' : (finalValue.includes('+') ? '+' : ''))); }, duration / 60); }, /** * Animate feature cards with micro-interactions */ animateFeatureCard($card) { const $icon = $card.find('.yatra-feature-icon, .yatra-showcase-icon'); // Add bounce effect to icon setTimeout(() => { $icon.css('animation', 'bounceIn 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55)'); }, 200); }, /** * Bind event handlers */ bindEvents() { // Smooth scroll for anchor links this.elements.$document.on('click', 'a[href^="#"]', this.handleSmoothScroll.bind(this)); // Navigation scroll effects this.elements.$window.on('scroll', this.throttle(this.handleNavScroll.bind(this), 16)); // Parallax scroll effects this.elements.$window.on('scroll', this.throttle(this.handleParallaxScroll.bind(this), 16)); // Card hover effects this.elements.$featureCards.on('mouseenter', this.handleCardHover.bind(this)); this.elements.$showcaseCards.on('mouseenter', this.handleShowcaseHover.bind(this)); // Button interactions this.elements.$ctaButtons.on('mouseenter', this.handleButtonHover.bind(this)); // Window resize this.elements.$window.on('resize', this.debounce(this.handleResize.bind(this), 250)); // Form interactions for metabox $('.yatra-premium-features-metabox').on('click', '.yatra-feature-card', this.handleMetaboxFeatureClick.bind(this)); }, /** * Handle smooth scrolling */ handleSmoothScroll(e) { const target = $(e.currentTarget).attr('href'); if (target.startsWith('#') && $(target).length) { e.preventDefault(); $('html, body').animate({ scrollTop: $(target).offset().top - this.config.scrollOffset }, this.config.animationDuration, 'easeInOutCubic'); } }, /** * Handle navigation scroll effects */ handleNavScroll() { const scrollY = this.elements.$window.scrollTop(); if (scrollY > 50) { this.elements.$nav.addClass('scrolled').css({ 'background': 'rgba(255, 255, 255, 0.98)', 'backdrop-filter': 'blur(20px)', 'box-shadow': '0 4px 20px rgba(0, 0, 0, 0.1)' }); } else { this.elements.$nav.removeClass('scrolled').css({ 'background': 'rgba(255, 255, 255, 0.95)', 'box-shadow': 'none' }); } }, /** * Handle parallax scroll effects */ handleParallaxScroll() { const scrollY = this.elements.$window.scrollTop(); this.elements.$parallaxElements.each((index, element) => { const $element = $(element); const rect = element.getBoundingClientRect(); const speed = $element.data('speed') || 0.5; if (rect.bottom >= 0 && rect.top <= this.state.windowHeight) { const yPos = -(scrollY * speed); $element.css('transform', `translateY(${yPos}px)`); } }); }, /** * Handle card hover effects */ handleCardHover(e) { const $card = $(e.currentTarget); const $icon = $card.find('.yatra-feature-icon'); $icon.css({ 'transform': 'rotate(0deg) scale(1.1)', 'transition': 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)' }); $card.on('mouseleave', () => { $icon.css('transform', 'rotate(-5deg) scale(1)'); }); }, /** * Handle showcase card hover */ handleShowcaseHover(e) { const $card = $(e.currentTarget); if (!$card.hasClass('featured')) { $card.css({ 'transform': 'translateY(-12px) scale(1.02)', 'box-shadow': '0 25px 50px -12px rgba(0, 0, 0, 0.25)' }); $card.on('mouseleave', () => { $card.css({ 'transform': 'translateY(0) scale(1)', 'box-shadow': 'none' }); }); } }, /** * Handle button hover effects */ handleButtonHover(e) { const $button = $(e.currentTarget); // Create ripple effect this.createRippleEffect($button, e); }, /** * Create ripple effect on buttons */ createRippleEffect($element, event) { const rect = $element[0].getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const $ripple = $(''); $ripple.css({ 'position': 'absolute', 'border-radius': '50%', 'background': 'rgba(255, 255, 255, 0.3)', 'transform': 'scale(0)', 'animation': 'ripple 0.6s linear', 'left': x - 25, 'top': y - 25, 'width': 50, 'height': 50, 'pointer-events': 'none' }); if (!$element.find('.ripple').length) { $element.css('position', 'relative').append($ripple); setTimeout(() => $ripple.remove(), 600); } }, /** * Handle metabox feature clicks */ handleMetaboxFeatureClick(e) { e.preventDefault(); const $card = $(e.currentTarget); const feature = $card.data('feature'); // Add click animation $card.css('transform', 'scale(0.98)'); setTimeout(() => { $card.css('transform', 'scale(1)'); }, 150); // Show upgrade modal or redirect this.showUpgradeNotification(feature); }, /** * Show upgrade notification */ showUpgradeNotification(feature) { const message = yatraPremium?.strings?.upgrading || 'Redirecting to upgrade...'; // Create toast notification const $toast = $(`