/** * 动画助手 - 处理滚动触发动画和交互动画 */ class AnimationHelper { constructor() { this.animatedElements = []; this.isInitialized = false; this.observer = null; this.init(); } /** * 初始化动画助手 */ init() { if (this.isInitialized) return; // 设置Intersection Observer this.setupIntersectionObserver(); // 添加滚动事件监听 this.addScrollListeners(); // 监听路由变化 this.setupRouteListener(); // 初始化页面加载动画 this.initPageLoadAnimations(); this.isInitialized = true; } /** * 设置Intersection Observer用于滚动触发动画 */ setupIntersectionObserver() { if (!window.IntersectionObserver) { // 降级处理:直接显示所有元素 this.showAllElements(); return; } this.observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { this.animateElement(entry.target); } }); }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); } /** * 添加滚动事件监听 */ addScrollListeners() { // 监听滚动事件,用于视差效果 window.addEventListener('scroll', this.handleScroll.bind(this), { passive: true }); // 监听窗口大小变化 window.addEventListener('resize', this.handleResize.bind(this), { passive: true }); } /** * 设置路由监听 */ setupRouteListener() { // 监听 popstate 事件(浏览器前进后退) window.addEventListener('popstate', this.handleRouteChange.bind(this)); // 监听自定义路由变化事件 window.addEventListener('routeChange', this.handleRouteChange.bind(this)); // 监听页面可见性变化 document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); } /** * 处理滚动事件 */ handleScroll() { this.updateParallaxEffects(); this.updateScrollProgress(); } /** * 处理窗口大小变化 */ handleResize() { // 重新计算动画元素位置 this.recalculateAnimations(); } /** * 处理路由变化 */ handleRouteChange() { // 延迟重置动画,确保DOM已更新 setTimeout(() => { this.resetAnimations(); this.initPageLoadAnimations(); }, 100); } /** * 处理页面可见性变化 */ handleVisibilityChange() { if (!document.hidden) { // 页面重新可见时,重置动画状态 setTimeout(() => { this.resetAnimations(); this.initPageLoadAnimations(); }, 100); } } /** * 初始化页面加载动画 */ initPageLoadAnimations() { // 为页面加载动画添加延迟 setTimeout(() => { this.animatePageLoad(); }, 100); } /** * 页面加载动画 */ animatePageLoad() { // 标题动画 const titles = document.querySelectorAll('.section-title.animate-on-scroll'); titles.forEach((title, index) => { setTimeout(() => { title.classList.add('visible'); }, index * 200); }); // 内容动画 const contentElements = document.querySelectorAll('.animate-stagger'); contentElements.forEach((container, containerIndex) => { const children = container.children; Array.from(children).forEach((child, childIndex) => { setTimeout(() => { child.style.opacity = '1'; child.style.transform = 'translateY(0)'; }, (containerIndex * 300) + (childIndex * 100)); }); }); } /** * 为元素添加滚动触发动画 */ addScrollAnimation(element, options = {}) { if (!element) return; const defaultOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px', animationClass: 'animate-in', delay: 0 }; const config = { ...defaultOptions, ...options }; // 添加动画类 element.classList.add('scroll-animate'); // 设置延迟 if (config.delay > 0) { element.style.transitionDelay = `${config.delay}s`; } // 添加到观察列表 if (this.observer) { this.observer.observe(element); } this.animatedElements.push(element); } /** * 为多个元素添加交错动画 */ addStaggerAnimation(container, options = {}) { if (!container) return; const defaultOptions = { delay: 0.1, staggerDelay: 0.1, animationClass: 'visible' }; const config = { ...defaultOptions, ...options }; // 添加交错动画类 container.classList.add('animate-stagger'); // 为子元素设置初始状态 const children = Array.from(container.children); children.forEach((child, index) => { child.style.opacity = '0'; child.style.transform = 'translateY(30px)'; child.style.transitionDelay = `${config.delay + (index * config.staggerDelay)}s`; }); // 添加到观察列表 if (this.observer) { this.observer.observe(container); } this.animatedElements.push(container); } /** * 触发元素动画 */ animateElement(element) { if (!element) return; // 添加可见类 if (element.classList.contains('scroll-animate')) { element.classList.add('animate-in'); } else if (element.classList.contains('animate-stagger')) { element.classList.add('visible'); } // 触发自定义事件 element.dispatchEvent(new CustomEvent('animationStart', { detail: { element, type: 'scroll' } })); } /** * 手动触发动画 */ triggerAnimation(element, animationClass = 'animate-in') { if (!element) return; element.classList.add(animationClass); // 触发自定义事件 element.dispatchEvent(new CustomEvent('animationTrigger', { detail: { element, animationClass } })); } /** * 重置元素动画状态 */ resetAnimation(element) { if (!element) return; element.classList.remove('animate-in', 'visible'); element.style.transitionDelay = ''; // 重置样式 if (element.classList.contains('scroll-animate')) { element.style.opacity = '0'; element.style.transform = 'translateY(50px)'; } else if (element.classList.contains('animate-stagger')) { const children = Array.from(element.children); children.forEach(child => { child.style.opacity = '0'; child.style.transform = 'translateY(30px)'; }); } } /** * 更新视差效果 */ updateParallaxEffects() { const parallaxElements = document.querySelectorAll('[data-parallax]'); const scrolled = window.pageYOffset; parallaxElements.forEach(element => { const speed = parseFloat(element.dataset.parallax) || 0.5; const yPos = -(scrolled * speed); element.style.transform = `translateY(${yPos}px)`; }); } /** * 更新滚动进度 */ updateScrollProgress() { const progressElements = document.querySelectorAll('[data-scroll-progress]'); const scrolled = window.pageYOffset; const maxScroll = document.documentElement.scrollHeight - window.innerHeight; const progress = (scrolled / maxScroll) * 100; progressElements.forEach(element => { if (element.dataset.scrollProgress === 'bar') { element.style.width = `${progress}%`; } else if (element.dataset.scrollProgress === 'number') { element.textContent = Math.round(progress); } }); } /** * 重新计算动画 */ recalculateAnimations() { // 重新观察所有元素 this.animatedElements.forEach(element => { if (this.observer) { this.observer.unobserve(element); this.observer.observe(element); } }); } /** * 重置所有动画状态 */ resetAnimations() { // 重置所有动画元素的类 this.animatedElements.forEach(element => { if (element.classList.contains('scroll-animate')) { element.classList.remove('animate-in'); } else if (element.classList.contains('animate-stagger')) { element.classList.remove('visible'); // 重置子元素状态 const children = Array.from(element.children); children.forEach((child, index) => { child.style.opacity = '0'; child.style.transform = 'translateY(30px)'; }); } }); // 重置所有带有动画类的元素 const allAnimatedElements = document.querySelectorAll('.animate-on-scroll, .animate-stagger, .scroll-animate'); allAnimatedElements.forEach(element => { element.classList.remove('visible', 'animate-in'); // 重置 transform 和 opacity if (element.classList.contains('animate-on-scroll')) { element.style.opacity = '0'; element.style.transform = 'translateY(30px)'; } }); // 重新观察所有元素 this.recalculateAnimations(); } /** * 显示所有元素(降级处理) */ showAllElements() { const scrollElements = document.querySelectorAll('.scroll-animate, .animate-stagger'); scrollElements.forEach(element => { if (element.classList.contains('scroll-animate')) { element.classList.add('animate-in'); } else if (element.classList.contains('animate-stagger')) { element.classList.add('visible'); } }); } /** * 添加鼠标悬停动画 */ addHoverAnimation(element, animationType = 'scale') { if (!element) return; const animations = { scale: 'hover-scale', lift: 'hover-lift', rotate: 'hover-rotate', glow: 'hover-glow', bounce: 'hover-bounce' }; const animationClass = animations[animationType] || 'hover-scale'; element.classList.add(animationClass); } /** * 添加点击动画 */ addClickAnimation(element, animationType = 'pulse') { if (!element) return; element.addEventListener('click', () => { element.classList.add(`animate ${animationType}`); // 动画结束后移除类 setTimeout(() => { element.classList.remove(`animate ${animationType}`); }, 1000); }); } /** * 销毁动画助手 */ destroy() { if (this.observer) { this.observer.disconnect(); } window.removeEventListener('scroll', this.handleScroll); window.removeEventListener('resize', this.handleResize); window.removeEventListener('popstate', this.handleRouteChange); window.removeEventListener('routeChange', this.handleRouteChange); document.removeEventListener('visibilitychange', this.handleVisibilityChange); this.animatedElements = []; this.isInitialized = false; } } // 创建全局实例 window.animationHelper = new AnimationHelper(); // 导出类(如果使用模块系统) if (typeof module !== 'undefined' && module.exports) { module.exports = AnimationHelper; }