aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
387 lines (384 loc) • 11.2 kB
JavaScript
import { canUseDOM } from './ssr.js';
// Performance monitoring
class PerformanceMonitor {
constructor(config = {}) {
this.observers = [];
this.frameCount = 0;
this.lastTime = 0;
this.measurements = new Map();
this.config = {
enableFPSMonitoring: true,
enableMemoryMonitoring: false,
enableNetworkMonitoring: false,
targetFPS: 60,
maxMemoryUsage: 100 * 1024 * 1024,
// 100MB
enableAdaptiveQuality: true,
enableVirtualization: true,
enableLazyLoading: true,
enableCaching: true,
enableCompression: false,
...config
};
this.metrics = {
fps: 0,
frameTime: 0,
memoryUsage: 0,
renderTime: 0,
layoutTime: 0,
paintTime: 0,
compositeTime: 0,
networkLatency: 0,
cacheHitRate: 0
};
this.initializeMonitoring();
}
static getInstance(config) {
if (!PerformanceMonitor.instance) {
PerformanceMonitor.instance = new PerformanceMonitor(config);
}
return PerformanceMonitor.instance;
}
initializeMonitoring() {
// Skip monitoring during SSR
if (!canUseDOM) return;
if (this.config.enableFPSMonitoring) {
this.startFPSMonitoring();
}
if (this.config.enableMemoryMonitoring && "memory" in performance) {
this.startMemoryMonitoring();
}
if (this.config.enableNetworkMonitoring) {
this.startNetworkMonitoring();
}
}
startFPSMonitoring() {
const measureFPS = timestamp => {
this.frameCount++;
if (this.lastTime === 0) {
this.lastTime = timestamp;
requestAnimationFrame(measureFPS);
return;
}
const deltaTime = timestamp - this.lastTime;
if (deltaTime >= 1000) {
// Update every second
this.metrics.fps = Math.round(this.frameCount * 1000 / deltaTime);
this.metrics.frameTime = deltaTime / this.frameCount;
this.frameCount = 0;
this.lastTime = timestamp;
}
requestAnimationFrame(measureFPS);
};
requestAnimationFrame(measureFPS);
}
startMemoryMonitoring() {
setInterval(() => {
const memory = performance.memory;
if (memory) {
this.metrics.memoryUsage = memory.usedJSHeapSize;
}
}, 5000); // Check every 5 seconds
}
startNetworkMonitoring() {
// Skip during SSR - no fetch API available
if (!canUseDOM) return;
// Monitor fetch requests
const originalFetch = window.fetch;
let requestCount = 0;
let cacheHits = 0;
window.fetch = async (...args) => {
const startTime = Date.now();
requestCount++;
try {
const response = await originalFetch(...args);
const duration = Date.now() - startTime;
// Estimate cache hit based on response time
if (duration < 50) {
cacheHits++;
}
this.metrics.networkLatency = duration;
this.metrics.cacheHitRate = cacheHits / requestCount * 100;
return response;
} catch (error) {
throw error;
}
};
}
getMetrics() {
return {
...this.metrics
};
}
isPerformanceGood() {
return this.metrics.fps >= this.config.targetFPS * 0.8 && this.metrics.memoryUsage <= this.config.maxMemoryUsage;
}
getOptimizationSuggestions() {
const suggestions = [];
if (this.metrics.fps < this.config.targetFPS * 0.8) {
suggestions.push("Consider reducing animation complexity");
suggestions.push("Enable virtualization for large lists");
suggestions.push("Reduce particle effects or simplify shaders");
}
if (this.metrics.memoryUsage > this.config.maxMemoryUsage * 0.8) {
suggestions.push("Implement memory cleanup");
suggestions.push("Reduce texture sizes");
suggestions.push("Unload unused assets");
}
if (this.metrics.renderTime > 16.67) {
// 60 FPS threshold
suggestions.push("Optimize render pipeline");
suggestions.push("Use CSS transforms instead of layout properties");
suggestions.push("Implement object pooling");
}
return suggestions;
}
startMeasure(name) {
this.measurements.set(name, {
startTime: performance.now()
});
}
stop() {
// Stop all measurements and log results
for (const [name, measurement] of this.measurements) {
if (!measurement.endTime) {
measurement.endTime = performance.now();
const duration = measurement.endTime - measurement.startTime;
console.log(`Performance measurement '${name}': ${duration.toFixed(2)}ms`);
}
}
this.measurements.clear();
}
getMeasurementDuration(name) {
const measurement = this.measurements.get(name);
if (measurement && measurement.endTime) {
return measurement.endTime - measurement.startTime;
}
return null;
}
}
// Memory management
class MemoryManager {
constructor(maxCacheSize = 50 * 1024 * 1024) {
this.cache = new Map();
this.currentCacheSize = 0;
// 50MB default
this.maxCacheSize = maxCacheSize;
}
static getInstance(maxCacheSize) {
if (!MemoryManager.instance) {
MemoryManager.instance = new MemoryManager(maxCacheSize);
}
return MemoryManager.instance;
}
set(key, value, size = 0) {
if (this.currentCacheSize + size > this.maxCacheSize) {
this.evictOldEntries(size);
}
this.cache.set(key, {
value,
size,
lastAccessed: Date.now()
});
this.currentCacheSize += size;
}
get(key) {
const entry = this.cache.get(key);
if (entry) {
entry.lastAccessed = Date.now();
return entry.value;
}
return null;
}
delete(key) {
const entry = this.cache.get(key);
if (entry) {
this.currentCacheSize -= entry.size;
return this.cache.delete(key);
}
return false;
}
clear() {
this.cache.clear();
this.currentCacheSize = 0;
}
evictOldEntries(requiredSize) {
const entries = Array.from(this.cache.entries()).sort((a, b) => a[1].lastAccessed - b[1].lastAccessed);
let freedSize = 0;
for (const [key, entry] of entries) {
if (freedSize >= requiredSize) break;
freedSize += entry.size;
this.cache.delete(key);
}
this.currentCacheSize -= freedSize;
}
getStats() {
return {
size: this.currentCacheSize,
entries: this.cache.size,
hitRate: 0 // Would need to track hits/misses
};
}
}
// Rendering optimizations
const renderingOptimizations = {
// Use transform instead of changing layout properties
useTransformForMovement: (element, x, y) => {
element.style.transform = `translate(${x}px, ${y}px)`;
element.style.willChange = "transform";
},
// Optimize for 60fps animations
optimizeFor60FPS: callback => {
let lastTime = 0;
const animate = timestamp => {
if (timestamp - lastTime >= 16.67) {
// ~60fps
callback();
lastTime = timestamp;
}
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
},
// Batch DOM updates
batchDOMUpdates: updates => {
requestAnimationFrame(() => {
updates.forEach(update => update());
});
},
// Use CSS containment for performance
enableCSSContainment: element => {
element.style.contain = "layout style paint";
},
// Optimize canvas rendering
optimizeCanvasRendering: canvas => {
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.imageSmoothingEnabled = false;
ctx.globalCompositeOperation = "source-over";
}
}
};
// Network optimizations
const networkOptimizations = {
// Lazy load images
lazyLoadImage: (img, src) => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
img.src = src;
observer.disconnect();
}
});
});
observer.observe(img);
},
// Preload critical resources
preloadCriticalResources: resources => {
// Skip during SSR - no document API available
if (!canUseDOM) return;
resources.forEach(resource => {
const link = document.createElement("link");
link.rel = "preload";
link.href = resource;
document.head.appendChild(link);
});
},
// Cache API wrapper
cacheAPI: {
async set(key, data) {
if ("caches" in window) {
const cache = await caches.open("glass-cache-v1");
await cache.put(key, new Response(JSON.stringify(data)));
}
},
async get(key) {
if ("caches" in window) {
const cache = await caches.open("glass-cache-v1");
const response = await cache.match(key);
if (response) {
return response.json();
}
}
return null;
}
}
};
// Virtualization utilities
class VirtualScroller {
constructor(container, itemHeight, totalItems, visibleItems) {
this.scrollTop = 0;
this.container = container;
this.itemHeight = itemHeight;
this.totalItems = totalItems;
this.visibleItems = visibleItems;
this.setupEventListeners();
}
setupEventListeners() {
this.container.addEventListener("scroll", this.handleScroll.bind(this));
}
handleScroll() {
this.scrollTop = this.container.scrollTop;
this.updateVisibleItems();
}
updateVisibleItems() {
const startIndex = Math.floor(this.scrollTop / this.itemHeight);
const endIndex = Math.min(startIndex + this.visibleItems, this.totalItems);
// Dispatch custom event with visible range
const event = new CustomEvent("virtualScroll", {
detail: {
startIndex,
endIndex
}
});
this.container.dispatchEvent(event);
}
getVisibleRange() {
const start = Math.floor(this.scrollTop / this.itemHeight);
const end = Math.min(start + this.visibleItems, this.totalItems);
return {
start,
end
};
}
scrollToItem(index) {
this.container.scrollTop = index * this.itemHeight;
}
}
// Adaptive quality system
class AdaptiveQuality {
constructor(monitor) {
this.qualityLevel = 1.0;
this.monitor = monitor;
}
updateQuality() {
const metrics = this.monitor.getMetrics();
const targetFPS = 60;
const fpsRatio = metrics.fps / targetFPS;
if (fpsRatio < 0.8) {
// Reduce quality
this.qualityLevel = Math.max(0.1, this.qualityLevel * 0.9);
} else if (fpsRatio > 1.2) {
// Increase quality
this.qualityLevel = Math.min(1.0, this.qualityLevel * 1.05);
}
return this.qualityLevel;
}
getQualitySettings() {
return {
particleCount: Math.floor(1000 * this.qualityLevel),
textureSize: Math.floor(1024 * this.qualityLevel),
shadowQuality: Math.floor(4 * this.qualityLevel),
antialiasing: this.qualityLevel > 0.5
};
}
}
// Export utilities
const performanceUtils = {
monitor: PerformanceMonitor.getInstance,
memory: MemoryManager.getInstance,
adaptiveQuality: monitor => new AdaptiveQuality(monitor),
virtualScroller: VirtualScroller
};
export { AdaptiveQuality, MemoryManager, PerformanceMonitor, VirtualScroller, networkOptimizations, performanceUtils, renderingOptimizations };
//# sourceMappingURL=performanceOptimizations.js.map