<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web Worker 优势演示</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
}
.container {
max-width: 900px;
width: 100%;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 40px;
}
h1 {
color: #333;
text-align: center;
margin-bottom: 10px;
font-size: 32px;
}
.subtitle {
text-align: center;
color: #666;
margin-bottom: 40px;
font-size: 16px;
}
.demo-section {
margin-bottom: 30px;
padding: 25px;
background: #f8f9fa;
border-radius: 12px;
border: 2px solid #e9ecef;
}
.demo-section h2 {
color: #495057;
margin-bottom: 15px;
font-size: 20px;
display: flex;
align-items: center;
}
.demo-section h2::before {
content: '';
display: inline-block;
width: 4px;
height: 20px;
background: #667eea;
margin-right: 10px;
border-radius: 2px;
}
.demo-section p {
color: #6c757d;
margin-bottom: 20px;
line-height: 1.6;
}
.button-group {
display: flex;
gap: 15px;
flex-wrap: wrap;
}
button {
flex: 1;
min-width: 150px;
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
border: none;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
color: white;
}
.btn-block {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.btn-block:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(245, 87, 108, 0.4);
}
.btn-worker {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.btn-worker:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(79, 172, 254, 0.4);
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
}
.status {
margin-top: 15px;
padding: 15px;
background: white;
border-radius: 8px;
min-height: 80px;
border-left: 4px solid #667eea;
}
.status-title {
font-weight: 600;
color: #495057;
margin-bottom: 8px;
}
.status-content {
color: #6c757d;
font-size: 14px;
line-height: 1.6;
}
.highlight {
color: #667eea;
font-weight: 600;
}
.success {
color: #28a745;
font-weight: 600;
}
.animation-test {
margin-top: 20px;
padding: 20px;
background: white;
border-radius: 8px;
text-align: center;
}
.animation-test h3 {
color: #495057;
margin-bottom: 15px;
font-size: 16px;
}
.spinner {
width: 50px;
height: 50px;
margin: 0 auto;
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
/* 不使用CSS动画,改用JavaScript驱动 */
}
.fps-counter {
margin-top: 10px;
font-size: 18px;
font-weight: bold;
color: #667eea;
}
.frame-indicator {
display: inline-block;
width: 20px;
height: 20px;
background: #28a745;
border-radius: 50%;
margin-left: 10px;
animation: pulse 1s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.8); }
}
.info-box {
margin-top: 30px;
padding: 20px;
background: #e7f3ff;
border-radius: 12px;
border-left: 4px solid #0066cc;
}
.info-box h3 {
color: #0066cc;
margin-bottom: 10px;
font-size: 18px;
}
.info-box ul {
list-style: none;
padding-left: 0;
}
.info-box li {
color: #495057;
margin-bottom: 8px;
padding-left: 24px;
position: relative;
line-height: 1.6;
}
.info-box li::before {
content: '✓';
position: absolute;
left: 0;
color: #28a745;
font-weight: bold;
}
.progress-bar {
width: 100%;
height: 8px;
background: #e9ecef;
border-radius: 4px;
overflow: hidden;
margin-top: 10px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
width: 0%;
transition: width 0.3s ease;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Web Worker 优势演示</h1>
<p class="subtitle">对比主线程计算 vs Web Worker 后台计算</p>
<!-- 主线程计算 -->
<div class="demo-section">
<h2>❌ 主线程计算(造成卡顿)</h2>
<p>点击按钮后,将执行1亿次密集计算,分成50批在主线程执行。这会<strong>造成严重卡顿</strong>,动画变得断断续续,FPS大幅下降,页面响应缓慢。</p>
<div class="button-group">
<button class="btn-block" onclick="calculateInMainThread()">
开始主线程计算
</button>
</div>
<div class="status" id="mainThreadStatus">
<div class="status-title">状态:</div>
<div class="status-content">等待开始...</div>
</div>
</div>
<!-- Web Worker 计算 -->
<div class="demo-section">
<h2>✅ Web Worker 计算(流畅不卡)</h2>
<p>点击按钮后,执行完全相同的1亿次计算。计算会在Web Worker后台线程中执行,<strong>完全不卡顿</strong>,动画持续流畅,FPS保持60,还能实时更新进度。</p>
<div class="button-group">
<button class="btn-worker" onclick="calculateInWorker()">
开始Worker计算
</button>
</div>
<div class="status" id="workerStatus">
<div class="status-title">状态:</div>
<div class="status-content">等待开始...</div>
</div>
</div>
<!-- 动画测试区域 -->
<div class="animation-test">
<h3>🎯 UI响应性测试 - JavaScript动画(依赖主线程)</h3>
<div class="spinner" id="spinner"></div>
<div class="fps-counter">
FPS: <span id="fpsValue">60</span>
<span class="frame-indicator" id="frameIndicator"></span>
</div>
<p style="margin-top: 15px; color: #6c757d; font-size: 14px;">
主线程计算时:动画变得<strong>卡顿</strong>,FPS降到<strong>10-20</strong>,转动不流畅<br>
Worker计算时:动画<strong>完全流畅</strong>,FPS保持<strong>60</strong>,丝般顺滑
</p>
</div>
<!-- 技术说明 -->
<div class="info-box" style="background: #fff3cd; border-left-color: #ffc107;">
<h3 style="color: #856404;">⚙️ 技术实现说明</h3>
<ul>
<li><strong>JavaScript动画:</strong>使用 requestAnimationFrame 驱动动画,完全依赖主线程事件循环</li>
<li><strong>分批计算:</strong>主线程将计算分成50批,每批约100ms,通过setTimeout串联,造成持续卡顿</li>
<li><strong>卡顿原理:</strong>每批计算占用主线程时,RAF执行被延迟,动画更新不及时,FPS下降</li>
<li><strong>Worker优势:</strong>计算在后台线程,主线程完全空闲,动画帧正常执行,FPS保持60</li>
<li><strong>真实场景:</strong>这种卡顿更接近实际应用(如大数据处理、图像处理等)的体验</li>
</ul>
</div>
<!-- 对比说明 -->
<div class="info-box" style="background: #f8d7da; border-left-color: #f5576c;">
<h3 style="color: #721c24;">📊 效果对比</h3>
<ul style="list-style: none; padding-left: 0;">
<li style="margin-bottom: 12px;">
<strong style="color: #f5576c;">❌ 主线程计算:</strong><br>
<span style="padding-left: 24px; display: block;">动画卡顿、掉帧严重、FPS降到10-20、用户体验差、页面响应慢</span>
</li>
<li>
<strong style="color: #28a745;">✅ Worker计算:</strong><br>
<span style="padding-left: 24px; display: block;">动画流畅、不掉帧、FPS稳定60、用户体验好、页面完全响应</span>
</li>
</ul>
</div>
<!-- 信息说明 -->
<div class="info-box">
<h3>💡 Web Worker 的优势</h3>
<ul>
<li><strong>不阻塞UI:</strong>在后台线程执行代码,保持页面响应性</li>
<li><strong>更好的性能:</strong>充分利用多核CPU,提升应用性能</li>
<li><strong>改善用户体验:</strong>避免页面卡顿,动画和交互保持流畅</li>
<li><strong>适用场景:</strong>大数据处理、复杂计算、图像处理、加密解密、视频编码等</li>
</ul>
</div>
</div>
<script>
let worker = null;
let animationId = null;
let rotation = 0;
let lastFrameTime = performance.now();
let frameCount = 0;
let fps = 60;
// 使用 requestAnimationFrame 驱动的 JavaScript 动画
function animateSpinner() {
const spinner = document.getElementById('spinner');
const fpsValue = document.getElementById('fpsValue');
const now = performance.now();
// 更新旋转角度
rotation += 3; // 每帧旋转3度
if (rotation >= 360) rotation -= 360;
spinner.style.transform = `rotate(${rotation}deg)`;
// 计算FPS(每秒更新一次)
frameCount++;
if (now - lastFrameTime >= 1000) {
fps = Math.round((frameCount * 1000) / (now - lastFrameTime));
fpsValue.textContent = fps;
frameCount = 0;
lastFrameTime = now;
}
// 继续下一帧动画
animationId = requestAnimationFrame(animateSpinner);
}
// 启动动画
function startAnimation() {
if (!animationId) {
animateSpinner();
}
}
// 停止动画(仅用于调试,实际不需要)
function stopAnimation() {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
}
// 创建内联的 Web Worker
function createInlineWorker() {
const workerCode = `
// Web Worker 代码
// 这个代码会在独立的后台线程中运行,不会阻塞主线程
// 监听主线程发来的消息
self.onmessage = function (e) {
const { totalIterations, batchSize } = e.data;
const startTime = performance.now();
let iterations = 0;
let sum = 0;
// 分批执行计算
function processBatch() {
const batchEnd = Math.min(iterations + batchSize, totalIterations);
// 执行一批计算
for (let i = iterations; i < batchEnd; i++) {
sum += Math.sqrt(i) + Math.sin(i) + Math.cos(i);
if (i % 1000000 === 0) {
sum = sum % 1000000;
}
}
iterations = batchEnd;
const progress = Math.floor((iterations / totalIterations) * 100);
// 报告进度
self.postMessage({
type: "progress",
progress: progress,
iterations: iterations,
elapsed: (performance.now() - startTime).toFixed(0),
});
// 检查是否完成
if (iterations < totalIterations) {
// 继续下一批,使用setTimeout让Worker可以处理其他任务
setTimeout(processBatch, 0);
} else {
// 计算完成
const endTime = performance.now();
const duration = (endTime - startTime).toFixed(2);
self.postMessage({
type: "complete",
iterations: iterations,
sum: sum,
time: duration,
});
}
}
// 开始处理
processBatch();
};
// 错误处理
self.onerror = function (error) {
console.error("Worker 内部错误:", error);
self.postMessage({
type: "error",
message: error.message,
});
};
`;
// 创建 Blob 对象
const blob = new Blob([workerCode], { type: 'application/javascript' });
// 创建 Worker URL
const workerUrl = URL.createObjectURL(blob);
// 创建 Worker 实例
return new Worker(workerUrl);
}
// 初始化 Web Worker
function initWorker() {
if (!worker) {
worker = createInlineWorker();
worker.onmessage = function(e) {
const statusDiv = document.getElementById('workerStatus');
if (e.data.type === 'progress') {
statusDiv.innerHTML = `
<div class="status-title">状态:计算中...</div>
<div class="status-content">
进度: <span class="highlight">${e.data.progress}%</span><br>
已执行: <span class="highlight">${e.data.iterations.toLocaleString()}</span> 次迭代<br>
已耗时: <span class="highlight">${e.data.elapsed}ms</span><br>
<span style="color: #28a745;">✓ UI完全流畅,FPS保持60,动画不卡顿</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: ${e.data.progress}%"></div>
</div>
`;
} else if (e.data.type === 'complete') {
statusDiv.innerHTML = `
<div class="status-title">状态:完成 ✓</div>
<div class="status-content">
<span class="success">计算完成!</span><br>
执行迭代: <span class="highlight">${e.data.iterations.toLocaleString()}</span> 次<br>
计算结果: <span class="highlight">${e.data.sum.toFixed(2)}</span><br>
总耗时: <span class="highlight">${e.data.time}ms</span> (约${(e.data.time/1000).toFixed(1)}秒)<br>
<span style="color: #28a745;">✓ 整个过程UI始终流畅,FPS保持60,无任何卡顿</span>
</div>
`;
}
};
worker.onerror = function(error) {
console.error('Worker error:', error);
document.getElementById('workerStatus').innerHTML = `
<div class="status-title">错误:</div>
<div class="status-content" style="color: #dc3545;">
${error.message}
</div>
`;
};
}
}
// 主线程计算(会造成卡顿)
function calculateInMainThread() {
const statusDiv = document.getElementById('mainThreadStatus');
statusDiv.innerHTML = `
<div class="status-title">状态:计算中...</div>
<div class="status-content">
<span style="color: #f5576c;">⚠️ 主线程计算中,观察FPS下降和动画卡顿</span><br>
进度: <span id="mainProgress">0</span>% | 批次: <span id="mainBatch">0</span>/50
</div>
<div class="progress-bar">
<div class="progress-fill" id="mainProgressBar" style="width: 0%"></div>
</div>
`;
const startTime = performance.now();
let iterations = 0;
let sum = 0;
let currentStep = 0;
const totalSteps = 50; // 分成50个批次
const iterationsPerStep = 2000000; // 每批次200万次迭代
// 使用分批计算模拟卡顿效果
function doCalculationBatch() {
// 执行一批密集计算(阻塞约100ms)
const batchStart = performance.now();
for (let i = 0; i < iterationsPerStep; i++) {
sum += Math.sqrt(iterations) + Math.sin(iterations) + Math.cos(iterations);
iterations++;
if (iterations % 1000000 === 0) {
sum = sum % 1000000;
}
}
currentStep++;
const progress = Math.floor((currentStep / totalSteps) * 100);
// 更新进度
document.getElementById('mainProgress').textContent = progress;
document.getElementById('mainBatch').textContent = currentStep;
document.getElementById('mainProgressBar').style.width = progress + '%';
if (currentStep < totalSteps) {
// 立即执行下一批,造成持续卡顿
// 使用setTimeout(fn, 0)让浏览器有机会渲染,但仍然很卡
setTimeout(doCalculationBatch, 0);
} else {
// 计算完成
const endTime = performance.now();
const duration = (endTime - startTime).toFixed(2);
statusDiv.innerHTML = `
<div class="status-title">状态:完成 ✓</div>
<div class="status-content">
<span class="success">计算完成!</span><br>
执行迭代: <span class="highlight">${iterations.toLocaleString()}</span> 次<br>
计算结果: <span class="highlight">${sum.toFixed(2)}</span><br>
总耗时: <span class="highlight">${duration}ms</span> (约${(duration/1000).toFixed(1)}秒)<br>
<span style="color: #f5576c;">⚠️ 计算期间UI严重卡顿,FPS大幅下降</span>
</div>
`;
}
}
// 开始第一批计算
setTimeout(doCalculationBatch, 100);
}
// Web Worker 计算(不会阻塞UI)
function calculateInWorker() {
initWorker();
const statusDiv = document.getElementById('workerStatus');
statusDiv.innerHTML = `
<div class="status-title">状态:启动中...</div>
<div class="status-content">
正在启动Web Worker...<br>
<span style="color: #28a745;">✓ UI保持响应</span>
</div>
`;
// 发送计算任务到 Worker(相同的计算量:1亿次迭代)
worker.postMessage({
totalIterations: 100000000, // 1亿次迭代
batchSize: 2000000 // 每批200万次,与主线程一致
});
}
// 页面加载完成后初始化
window.addEventListener('load', () => {
console.log('Web Worker Demo 已加载');
// 启动JavaScript动画
startAnimation();
});
// 页面关闭时清理 Worker
window.addEventListener('beforeunload', () => {
if (worker) {
worker.terminate();
}
});
</script>
</body>
</html>