// ==UserScript== // @name 帧率检测器 - FPS Monitor (精简版) // @namespace http://tampermonkey.net/ // @version 1.2 // @description 实时检测网页帧率(FPS) - 左下角小窗显示 // @author Your Name // @match *://*/* // @grant none // @run-at document-end // @license All Rights Reserved // ==/UserScript== (function() { 'use strict'; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } function init() { // ---- 创建精简悬浮窗 ---- const container = document.createElement('div'); container.id = 'fps-monitor-root'; container.style.cssText = ` position: fixed; bottom: 20px; left: 20px; z-index: 999999; background: rgba(20, 22, 30, 0.85); backdrop-filter: blur(4px); border: 1px solid rgba(255,255,255,0.15); border-radius: 12px; padding: 10px 14px; min-width: 120px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; color: #e0e0e0; box-shadow: 0 4px 16px rgba(0,0,0,0.5); cursor: move; user-select: none; display: flex; flex-direction: column; gap: 4px; `; // 简洁布局:第一行 FPS值 + 状态,第二行 平均/最低/最高 container.innerHTML = `
-- 检测中
📊 -- -- -- #0
`; document.body.appendChild(container); // ---- 拖拽功能 ---- let isDragging = false; let startX, startY, origX, origY; container.addEventListener('mousedown', (e) => { isDragging = true; const rect = container.getBoundingClientRect(); startX = e.clientX; startY = e.clientY; origX = rect.left; origY = rect.top; container.style.cursor = 'grabbing'; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; let left = origX + (e.clientX - startX); let top = origY + (e.clientY - startY); // 限制边界 left = Math.max(0, Math.min(window.innerWidth - container.offsetWidth, left)); top = Math.max(0, Math.min(window.innerHeight - container.offsetHeight, top)); container.style.left = left + 'px'; container.style.bottom = 'auto'; container.style.top = top + 'px'; }); document.addEventListener('mouseup', () => { isDragging = false; container.style.cursor = 'move'; }); // ---- FPS 检测逻辑 ---- let lastTime = performance.now(); let frameCount = 0; let currentFps = 0; let totalFps = 0; let minFps = Infinity; let maxFps = -Infinity; let sampleCount = 0; let animationId = null; const fpsValueEl = document.getElementById('fpsValue'); const fpsStatusEl = document.getElementById('fpsStatus'); const avgValueEl = document.getElementById('avgValue'); const minValueEl = document.getElementById('minValue'); const maxValueEl = document.getElementById('maxValue'); const sampleCountEl = document.getElementById('sampleCount'); function updateDisplay(fps) { const rounded = Math.round(fps); fpsValueEl.textContent = rounded; // 状态颜色 if (fps >= 55) { fpsValueEl.style.color = '#6fcf97'; fpsStatusEl.textContent = '流畅'; fpsStatusEl.style.background = 'rgba(111, 207, 151, 0.25)'; } else if (fps >= 30) { fpsValueEl.style.color = '#f2c94a'; fpsStatusEl.textContent = '一般'; fpsStatusEl.style.background = 'rgba(242, 201, 74, 0.25)'; } else { fpsValueEl.style.color = '#eb5757'; fpsStatusEl.textContent = '卡顿'; fpsStatusEl.style.background = 'rgba(235, 87, 87, 0.25)'; } } function updateStats() { if (sampleCount === 0) { avgValueEl.textContent = '--'; minValueEl.textContent = '--'; maxValueEl.textContent = '--'; sampleCountEl.textContent = '0'; return; } const avg = totalFps / sampleCount; avgValueEl.textContent = avg.toFixed(1) + 'f'; minValueEl.textContent = Math.round(minFps) + 'f'; maxValueEl.textContent = Math.round(maxFps) + 'f'; sampleCountEl.textContent = sampleCount; } function recordFps(fps) { if (fps <= 0 || !isFinite(fps)) return; totalFps += fps; if (fps < minFps) minFps = fps; if (fps > maxFps) maxFps = fps; sampleCount++; updateStats(); } function tick(now) { frameCount++; const delta = now - lastTime; if (delta >= 1000) { currentFps = (frameCount * 1000) / delta; currentFps = Math.min(144, Math.max(0, currentFps)); if (currentFps > 0 && isFinite(currentFps)) { updateDisplay(currentFps); recordFps(currentFps); } frameCount = 0; lastTime = now; } animationId = requestAnimationFrame(tick); } function startMonitoring() { if (animationId) cancelAnimationFrame(animationId); lastTime = performance.now(); frameCount = 0; animationId = requestAnimationFrame(tick); } // 页面可见性变化时重置时间基准,防止切回来时FPS突变 document.addEventListener('visibilitychange', () => { if (!document.hidden) { lastTime = performance.now(); frameCount = 0; } }); // 点击FPS数值可重置统计数据(简易交互) fpsValueEl.addEventListener('dblclick', () => { totalFps = 0; minFps = Infinity; maxFps = -Infinity; sampleCount = 0; updateStats(); fpsStatusEl.textContent = '重置'; setTimeout(() => { if (currentFps >= 55) fpsStatusEl.textContent = '流畅'; else if (currentFps >= 30) fpsStatusEl.textContent = '一般'; else if (currentFps > 0) fpsStatusEl.textContent = '卡顿'; else fpsStatusEl.textContent = '检测中'; }, 600); }); startMonitoring(); window.addEventListener('beforeunload', () => { if (animationId) cancelAnimationFrame(animationId); }); console.log('FPS 检测器 (精简版) 已启动 - 左下角显示'); } })();