-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
<title>FPS 计算器演示</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 600px;
width: 100%;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
font-size: 32px;
}
.fps-display {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 15px;
padding: 30px;
margin-bottom: 30px;
text-align: center;
color: white;
}
.fps-value {
font-size: 72px;
font-weight: bold;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
}
.fps-label {
font-size: 18px;
opacity: 0.9;
}
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-bottom: 25px;
}
.stat-item {
background: #f5f5f5;
padding: 20px;
border-radius: 10px;
text-align: center;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #667eea;
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: #666;
}
.controls {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
button {
padding: 12px 25px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover {
background: #5568d3;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #e0e0e0;
color: #333;
}
.btn-secondary:hover {
background: #d0d0d0;
transform: translateY(-2px);
}
.info {
margin-top: 25px;
padding: 15px;
background: #f9f9f9;
border-radius: 8px;
font-size: 14px;
color: #666;
line-height: 1.6;
}
.canvas-container {
margin-top: 20px;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
canvas {
display: block;
width: 100%;
background: white;
}
</style>
🎮 FPS 计算器
<div class="fps-display">
<div class="fps-value" id="fpsValue">0</div>
<div class="fps-label">当前 FPS</div>
</div>
<div class="stats">
<div class="stat-item">
<div class="stat-value" id="avgFps">0</div>
<div class="stat-label">平均 FPS</div>
</div>
<div class="stat-item">
<div class="stat-value" id="minFps">0</div>
<div class="stat-label">最低 FPS</div>
</div>
<div class="stat-item">
<div class="stat-value" id="maxFps">0</div>
<div class="stat-label">最高 FPS</div>
</div>
</div>
<div class="controls">
<button class="btn-primary" id="resetBtn">重置统计</button>
<button class="btn-secondary" id="toggleLoad">增加负载</button>
</div>
<div class="canvas-container">
<canvas id="animationCanvas" width="520" height="200"></canvas>
</div>
<div class="info">
<strong>💡 说明:</strong><br>
FPS(Frames Per Second)表示每秒渲染的帧数。点击"增加负载"可以看到性能变化。
本示例使用 <code>requestAnimationFrame</code> 方法实现帧率计算。
</div>
</div>
<script>
// FPS 计算器类
class FPSCounter {
constructor() {
this.fps = 0;
this.frames = [];
this.lastFrameTime = performance.now();
this.allFps = [];
}
// 更新 FPS
update() {
const now = performance.now();
const delta = now - this.lastFrameTime;
this.lastFrameTime = now;
// 计算当前帧率
const currentFps = 1000 / delta;
this.frames.push(currentFps);
// 保持最近 60 帧的数据用于平滑计算
if (this.frames.length > 60) {
this.frames.shift();
}
// 计算平均 FPS(最近 60 帧)
this.fps = Math.round(
this.frames.reduce((a, b) => a + b, 0) / this.frames.length
);
// 记录所有 FPS 用于统计
this.allFps.push(this.fps);
return this.fps;
}
// 获取平均 FPS
getAverage() {
if (this.allFps.length === 0) return 0;
const sum = this.allFps.reduce((a, b) => a + b, 0);
return Math.round(sum / this.allFps.length);
}
// 获取最小 FPS
getMin() {
if (this.allFps.length === 0) return 0;
return Math.min(...this.allFps);
}
// 获取最大 FPS
getMax() {
if (this.allFps.length === 0) return 0;
return Math.max(...this.allFps);
}
// 重置统计
reset() {
this.frames = [];
this.allFps = [];
this.fps = 0;
}
}
// 初始化
const fpsCounter = new FPSCounter();
const fpsElement = document.getElementById('fpsValue');
const avgFpsElement = document.getElementById('avgFps');
const minFpsElement = document.getElementById('minFps');
const maxFpsElement = document.getElementById('maxFps');
const resetBtn = document.getElementById('resetBtn');
const toggleLoadBtn = document.getElementById('toggleLoad');
const canvas = document.getElementById('animationCanvas');
const ctx = canvas.getContext('2d');
let highLoad = false;
let particles = [];
// 粒子类(用于演示动画)
class Particle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.vx = (Math.random() - 0.5) * 3;
this.vy = (Math.random() - 0.5) * 3;
this.radius = Math.random() * 3 + 2;
this.color = `hsl(${Math.random() * 360}, 70%, 60%)`;
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x < 0 || this.x > canvas.width) this.vx *= -1;
if (this.y < 0 || this.y > canvas.height) this.vy *= -1;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
}
}
// 初始化粒子
function initParticles(count) {
particles = [];
for (let i = 0; i < count; i++) {
particles.push(new Particle());
}
}
initParticles(50);
// 主动画循环
function animate() {
// 更新 FPS
const currentFps = fpsCounter.update();
// 更新显示
fpsElement.textContent = currentFps;
avgFpsElement.textContent = fpsCounter.getAverage();
minFpsElement.textContent = fpsCounter.getMin();
maxFpsElement.textContent = fpsCounter.getMax();
// 根据 FPS 更改颜色
if (currentFps >= 50) {
fpsElement.style.color = '#4ade80';
} else if (currentFps >= 30) {
fpsElement.style.color = '#fbbf24';
} else {
fpsElement.style.color = '#f87171';
}
// 清空画布
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 更新和绘制粒子
particles.forEach(particle => {
particle.update();
particle.draw();
});
// 如果启用高负载,执行额外计算
if (highLoad) {
for (let i = 0; i < 100000; i++) {
Math.sqrt(Math.random() * 10000);
}
}
// 继续动画循环
requestAnimationFrame(animate);
}
// 重置按钮
resetBtn.addEventListener('click', () => {
fpsCounter.reset();
fpsElement.textContent = '0';
avgFpsElement.textContent = '0';
minFpsElement.textContent = '0';
maxFpsElement.textContent = '0';
});
// 切换负载按钮
toggleLoadBtn.addEventListener('click', () => {
highLoad = !highLoad;
if (highLoad) {
toggleLoadBtn.textContent = '减少负载';
toggleLoadBtn.classList.remove('btn-secondary');
toggleLoadBtn.classList.add('btn-primary');
initParticles(200); // 增加粒子数量
} else {
toggleLoadBtn.textContent = '增加负载';
toggleLoadBtn.classList.remove('btn-primary');
toggleLoadBtn.classList.add('btn-secondary');
initParticles(50); // 恢复粒子数量
}
});
// 启动动画
animate();
</script>
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels