// ==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 检测器 (精简版) 已启动 - 左下角显示');
}
})();