|
|
/**
|
|
|
* 性能监控器
|
|
|
* Performance Monitor
|
|
|
*
|
|
|
* 监控和优化JavaScript性能,特别是动画和滚动性能
|
|
|
* Monitor and optimize JavaScript performance, especially animations and scrolling
|
|
|
*
|
|
|
* @package Nenghui Energy Theme
|
|
|
* @since 1.0.0
|
|
|
*/
|
|
|
|
|
|
(function() {
|
|
|
'use strict';
|
|
|
|
|
|
// 性能监控器
|
|
|
const PerformanceMonitor = {
|
|
|
metrics: {
|
|
|
animations: [],
|
|
|
scrollEvents: 0,
|
|
|
memoryUsage: [],
|
|
|
renderTime: [],
|
|
|
gsapAnimations: 0,
|
|
|
elementorConflicts: 0
|
|
|
},
|
|
|
|
|
|
observers: {
|
|
|
performance: null,
|
|
|
memory: null,
|
|
|
intersection: null
|
|
|
},
|
|
|
|
|
|
// 初始化
|
|
|
init: function() {
|
|
|
if (this.initialized) return;
|
|
|
|
|
|
this.setupPerformanceObserver();
|
|
|
this.setupMemoryMonitoring();
|
|
|
this.setupAnimationMonitoring();
|
|
|
this.setupScrollOptimization();
|
|
|
this.setupIntersectionObserver();
|
|
|
|
|
|
this.initialized = true;
|
|
|
},
|
|
|
|
|
|
// 设置性能观察器
|
|
|
setupPerformanceObserver: function() {
|
|
|
if (!window.PerformanceObserver) return;
|
|
|
|
|
|
try {
|
|
|
this.observers.performance = new PerformanceObserver((list) => {
|
|
|
const entries = list.getEntries();
|
|
|
entries.forEach(entry => {
|
|
|
if (entry.entryType === 'measure') {
|
|
|
this.metrics.renderTime.push({
|
|
|
name: entry.name,
|
|
|
duration: entry.duration,
|
|
|
timestamp: Date.now()
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
this.observers.performance.observe({ entryTypes: ['measure', 'navigation'] });
|
|
|
} catch (e) {
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 设置内存监控
|
|
|
setupMemoryMonitoring: function() {
|
|
|
if (!performance.memory) return;
|
|
|
|
|
|
const monitorMemory = () => {
|
|
|
const memory = performance.memory;
|
|
|
this.metrics.memoryUsage.push({
|
|
|
used: memory.usedJSHeapSize,
|
|
|
total: memory.totalJSHeapSize,
|
|
|
limit: memory.jsHeapSizeLimit,
|
|
|
timestamp: Date.now()
|
|
|
});
|
|
|
|
|
|
// 保持最近100条记录
|
|
|
if (this.metrics.memoryUsage.length > 100) {
|
|
|
this.metrics.memoryUsage.shift();
|
|
|
}
|
|
|
|
|
|
// 检查内存泄漏
|
|
|
this.checkMemoryLeaks();
|
|
|
};
|
|
|
|
|
|
// 每30秒监控一次内存
|
|
|
setInterval(monitorMemory, 30000);
|
|
|
monitorMemory(); // 立即执行一次
|
|
|
},
|
|
|
|
|
|
// 设置动画监控
|
|
|
setupAnimationMonitoring: function() {
|
|
|
// 监控GSAP动画
|
|
|
if (window.gsap) {
|
|
|
const originalTo = gsap.to;
|
|
|
const originalFrom = gsap.from;
|
|
|
const originalFromTo = gsap.fromTo;
|
|
|
|
|
|
gsap.to = (...args) => {
|
|
|
this.metrics.gsapAnimations++;
|
|
|
return originalTo.apply(gsap, args);
|
|
|
};
|
|
|
|
|
|
gsap.from = (...args) => {
|
|
|
this.metrics.gsapAnimations++;
|
|
|
return originalFrom.apply(gsap, args);
|
|
|
};
|
|
|
|
|
|
gsap.fromTo = (...args) => {
|
|
|
this.metrics.gsapAnimations++;
|
|
|
return originalFromTo.apply(gsap, args);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
// 监控CSS动画
|
|
|
document.addEventListener('animationstart', (e) => {
|
|
|
this.metrics.animations.push({
|
|
|
type: 'css',
|
|
|
name: e.animationName,
|
|
|
target: e.target.tagName,
|
|
|
timestamp: Date.now()
|
|
|
});
|
|
|
});
|
|
|
},
|
|
|
|
|
|
// 设置滚动优化
|
|
|
setupScrollOptimization: function() {
|
|
|
let scrollTimeout;
|
|
|
let lastScrollTime = 0;
|
|
|
|
|
|
const optimizedScrollHandler = (e) => {
|
|
|
const now = Date.now();
|
|
|
|
|
|
// 限制滚动事件频率
|
|
|
if (now - lastScrollTime < 16) return; // 约60fps
|
|
|
|
|
|
lastScrollTime = now;
|
|
|
this.metrics.scrollEvents++;
|
|
|
|
|
|
// 清除之前的超时
|
|
|
clearTimeout(scrollTimeout);
|
|
|
|
|
|
// 滚动结束后的清理工作
|
|
|
scrollTimeout = setTimeout(() => {
|
|
|
this.optimizeAfterScroll();
|
|
|
}, 150);
|
|
|
};
|
|
|
|
|
|
window.addEventListener('scroll', optimizedScrollHandler, { passive: true });
|
|
|
},
|
|
|
|
|
|
// 设置交叉观察器
|
|
|
setupIntersectionObserver: function() {
|
|
|
if (!window.IntersectionObserver) return;
|
|
|
|
|
|
this.observers.intersection = new IntersectionObserver((entries) => {
|
|
|
entries.forEach(entry => {
|
|
|
const element = entry.target;
|
|
|
|
|
|
if (entry.isIntersecting) {
|
|
|
// 元素进入视口,可以启动动画
|
|
|
element.classList.add('in-viewport');
|
|
|
this.triggerLazyAnimations(element);
|
|
|
} else {
|
|
|
// 元素离开视口,可以暂停动画
|
|
|
element.classList.remove('in-viewport');
|
|
|
this.pauseAnimations(element);
|
|
|
}
|
|
|
});
|
|
|
}, {
|
|
|
rootMargin: '50px',
|
|
|
threshold: 0.1
|
|
|
});
|
|
|
|
|
|
// 观察所有动画元素
|
|
|
document.querySelectorAll('[data-animate], .gsap-animate, .elementor-invisible').forEach(el => {
|
|
|
this.observers.intersection.observe(el);
|
|
|
});
|
|
|
},
|
|
|
|
|
|
// 检查内存泄漏
|
|
|
checkMemoryLeaks: function() {
|
|
|
const recent = this.metrics.memoryUsage.slice(-10);
|
|
|
if (recent.length < 10) return;
|
|
|
|
|
|
const trend = recent.reduce((acc, curr, index) => {
|
|
|
if (index === 0) return acc;
|
|
|
return acc + (curr.used - recent[index - 1].used);
|
|
|
}, 0);
|
|
|
|
|
|
if (trend > 10 * 1024 * 1024) { // 10MB增长
|
|
|
console.warn('⚠️ 检测到潜在内存泄漏,内存使用持续增长');
|
|
|
this.cleanupMemory();
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 清理内存
|
|
|
cleanupMemory: function() {
|
|
|
// 清理过期的动画记录
|
|
|
const now = Date.now();
|
|
|
this.metrics.animations = this.metrics.animations.filter(anim =>
|
|
|
now - anim.timestamp < 300000 // 保留5分钟内的记录
|
|
|
);
|
|
|
|
|
|
// 清理过期的渲染时间记录
|
|
|
this.metrics.renderTime = this.metrics.renderTime.filter(render =>
|
|
|
now - render.timestamp < 300000
|
|
|
);
|
|
|
|
|
|
// 强制垃圾回收(如果可用)
|
|
|
if (window.gc) {
|
|
|
window.gc();
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 滚动后优化
|
|
|
optimizeAfterScroll: function() {
|
|
|
// 刷新ScrollTrigger
|
|
|
if (window.ScrollTrigger) {
|
|
|
ScrollTrigger.refresh();
|
|
|
}
|
|
|
|
|
|
// 清理不在视口内的动画
|
|
|
document.querySelectorAll('.gsap-animate:not(.in-viewport)').forEach(el => {
|
|
|
gsap.killTweensOf(el);
|
|
|
});
|
|
|
},
|
|
|
|
|
|
// 触发懒加载动画
|
|
|
triggerLazyAnimations: function(element) {
|
|
|
if (element.hasAttribute('data-lazy-animate')) {
|
|
|
const animationType = element.getAttribute('data-lazy-animate');
|
|
|
|
|
|
switch (animationType) {
|
|
|
case 'fadeIn':
|
|
|
gsap.fromTo(element,
|
|
|
{ opacity: 0, y: 30 },
|
|
|
{ opacity: 1, y: 0, duration: 0.6, ease: "power2.out" }
|
|
|
);
|
|
|
break;
|
|
|
case 'slideIn':
|
|
|
gsap.fromTo(element,
|
|
|
{ x: -50, opacity: 0 },
|
|
|
{ x: 0, opacity: 1, duration: 0.8, ease: "power2.out" }
|
|
|
);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
element.removeAttribute('data-lazy-animate');
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 暂停动画
|
|
|
pauseAnimations: function(element) {
|
|
|
if (window.gsap) {
|
|
|
gsap.killTweensOf(element);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 获取性能报告
|
|
|
getPerformanceReport: function() {
|
|
|
const memoryUsage = this.metrics.memoryUsage;
|
|
|
const currentMemory = memoryUsage[memoryUsage.length - 1];
|
|
|
|
|
|
return {
|
|
|
summary: {
|
|
|
totalAnimations: this.metrics.animations.length,
|
|
|
gsapAnimations: this.metrics.gsapAnimations,
|
|
|
scrollEvents: this.metrics.scrollEvents,
|
|
|
elementorConflicts: this.metrics.elementorConflicts
|
|
|
},
|
|
|
memory: {
|
|
|
current: currentMemory ? Math.round(currentMemory.used / 1024 / 1024) + 'MB' : 'N/A',
|
|
|
peak: memoryUsage.length > 0 ?
|
|
|
Math.round(Math.max(...memoryUsage.map(m => m.used)) / 1024 / 1024) + 'MB' : 'N/A'
|
|
|
},
|
|
|
performance: {
|
|
|
averageRenderTime: this.metrics.renderTime.length > 0 ?
|
|
|
Math.round(this.metrics.renderTime.reduce((sum, r) => sum + r.duration, 0) / this.metrics.renderTime.length) + 'ms' : 'N/A'
|
|
|
},
|
|
|
recommendations: this.getRecommendations()
|
|
|
};
|
|
|
},
|
|
|
|
|
|
// 获取优化建议
|
|
|
getRecommendations: function() {
|
|
|
const recommendations = [];
|
|
|
|
|
|
if (this.metrics.scrollEvents > 1000) {
|
|
|
recommendations.push('考虑进一步优化滚动事件处理,使用更大的节流间隔');
|
|
|
}
|
|
|
|
|
|
if (this.metrics.gsapAnimations > 50) {
|
|
|
recommendations.push('GSAP动画数量较多,考虑使用对象池或动画复用');
|
|
|
}
|
|
|
|
|
|
const memoryUsage = this.metrics.memoryUsage;
|
|
|
if (memoryUsage.length > 0) {
|
|
|
const latest = memoryUsage[memoryUsage.length - 1];
|
|
|
if (latest.used > latest.total * 0.8) {
|
|
|
recommendations.push('内存使用率较高,建议清理未使用的动画和事件监听器');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return recommendations;
|
|
|
},
|
|
|
|
|
|
// 销毁监控器
|
|
|
destroy: function() {
|
|
|
// 断开所有观察器
|
|
|
Object.values(this.observers).forEach(observer => {
|
|
|
if (observer && observer.disconnect) {
|
|
|
observer.disconnect();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 清理数据
|
|
|
this.metrics = {
|
|
|
animations: [],
|
|
|
scrollEvents: 0,
|
|
|
memoryUsage: [],
|
|
|
renderTime: [],
|
|
|
gsapAnimations: 0,
|
|
|
elementorConflicts: 0
|
|
|
};
|
|
|
|
|
|
this.initialized = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 等待DOM加载完成后初始化
|
|
|
if (document.readyState === 'loading') {
|
|
|
document.addEventListener('DOMContentLoaded', () => PerformanceMonitor.init());
|
|
|
} else {
|
|
|
PerformanceMonitor.init();
|
|
|
}
|
|
|
|
|
|
// 页面卸载时清理
|
|
|
window.addEventListener('beforeunload', () => PerformanceMonitor.destroy());
|
|
|
|
|
|
// 暴露到全局作用域
|
|
|
window.PerformanceMonitor = PerformanceMonitor;
|
|
|
|
|
|
// 开发模式下的调试功能
|
|
|
if (window.location.hostname === 'localhost' || window.location.search.includes('debug=1')) {
|
|
|
// 每分钟输出性能报告
|
|
|
setInterval(() => {
|
|
|
}, 60000);
|
|
|
|
|
|
// 添加全局调试命令
|
|
|
window.debugPerformance = () => PerformanceMonitor.getPerformanceReport();
|
|
|
}
|
|
|
|
|
|
})(); |