/** * Utility Functions */ /** * Format bytes to human-readable string */ function formatBytes(bytes, decimals = 2) { if (!bytes || bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; let i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k)); i = Math.max(0, Math.min(i, sizes.length - 1)); const value = bytes / Math.pow(k, i); return value.toFixed(decimals) + ' ' + sizes[i]; } /** * Format bytes per second to human-readable bandwidth */ function formatBandwidth(bytesPerSec, decimals = 2) { if (!bytesPerSec || bytesPerSec === 0) return '0 B/s'; const k = 1024; const sizes = ['B/s', 'KB/s', 'MB/s', 'GB/s', 'TB/s']; let i = Math.floor(Math.log(Math.abs(bytesPerSec)) / Math.log(k)); i = Math.max(0, Math.min(i, sizes.length - 1)); const value = bytesPerSec / Math.pow(k, i); return value.toFixed(decimals) + ' ' + sizes[i]; } /** * Format percentage */ function formatPercent(value, decimals = 1) { if (!value || isNaN(value)) return '0%'; return value.toFixed(decimals) + '%'; } /** * Format a timestamp to HH:MM */ function formatTime(timestamp) { const d = new Date(timestamp); return d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); } /** * Format full clock */ function formatClock() { const now = new Date(); const date = now.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }); const time = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); return `${date} ${time}`; } /** * Get color based on usage percentage */ function getUsageColor(percent) { if (percent < 50) return '#10b981'; if (percent < 75) return '#f59e0b'; return '#f43f5e'; } /** * Smooth number animation */ function animateValue(element, start, end, duration = 600) { const startTime = performance.now(); const diff = end - start; function update(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); // Ease out cubic const eased = 1 - Math.pow(1 - progress, 3); const current = start + diff * eased; if (element.dataset.format === 'percent') { element.textContent = formatPercent(current); } else if (element.dataset.format === 'bytes') { element.textContent = formatBytes(current); } else if (element.dataset.format === 'bandwidth') { element.textContent = formatBandwidth(current); } else { element.textContent = Math.round(current); } if (progress < 1) { requestAnimationFrame(update); } } requestAnimationFrame(update); }