Files
official_network/src/static/animation_helper.js
张成 2d0a8a4647 1
2025-08-25 17:55:01 +08:00

429 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 动画助手 - 处理滚动触发动画和交互动画
*/
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;
}