This commit is contained in:
张成
2025-08-25 15:28:08 +08:00
parent c4148d9eb6
commit 1ad48b234c
3 changed files with 382 additions and 29 deletions

View File

@@ -0,0 +1,349 @@
/**
* 动画助手 - 处理滚动触发动画和交互动画
*/
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.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 });
}
/**
* 处理滚动事件
*/
handleScroll() {
this.updateParallaxEffects();
this.updateScrollProgress();
}
/**
* 处理窗口大小变化
*/
handleResize() {
// 重新计算动画元素位置
this.recalculateAnimations();
}
/**
* 初始化页面加载动画
*/
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);
}
});
}
/**
* 显示所有元素(降级处理)
*/
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);
this.animatedElements = [];
this.isInitialized = false;
}
}
// 创建全局实例
window.animationHelper = new AnimationHelper();
// 导出类(如果使用模块系统)
if (typeof module !== 'undefined' && module.exports) {
module.exports = AnimationHelper;
}