概述
Lumin 博客在 探索 菜单下新增了 屏幕特效 页面,提供 14 种全屏趣味效果,涵盖恶作剧、屏保、助眠等场景。本文详解完整的实现方案,包括 Fullscreen API 全屏控制、Canvas 2D 动画渲染、Web Audio API 噪音生成等核心技术。
架构设计
1hugo.toml → 菜单注册(探索 → 屏幕特效,weight=28)
2content/screeneffects/_index.md → 页面元数据(type=screeneffects)
3layouts/screeneffects/single.html → 单页模板(内联 CSS + JS)
4layouts/_default/baseof.html → noSidebar 列表添加 screeneffects
5assets/css/main.css → :has(#screeneffects-page) 全宽布局选择器
6layouts/index.html → 首页排除 screeneffects 类型
采用 Hugo 的 Content Type 模式,所有效果逻辑内联在模板中,无需后端 API。
全屏机制
Fullscreen API
点击效果卡片时,先渲染效果内容,再请求浏览器全屏:
1function requestFS(el){
2 if(el.requestFullscreen) return el.requestFullscreen();
3 if(el.webkitRequestFullscreen) return el.webkitRequestFullscreen();
4 if(el.msRequestFullscreen) return el.msRequestFullscreen();
5 return Promise.resolve();
6}
7
8function showEffect(name){
9 // 1. 渲染效果到 overlay
10 overlay.style.display = 'block';
11 overlay.className = 'se-overlay active';
12
13 // 2. 渲染具体效果
14 effectBSOD(); // 或其他效果
15
16 // 3. 延迟请求全屏(需要元素已在 DOM 中)
17 setTimeout(function(){
18 requestFS(overlay).catch(function(){});
19 }, 50);
20}
关键点:全屏请求的目标是 overlay 自身而非 documentElement,这样全屏后只显示效果内容。
退出全屏
三种退出方式:
1// 1. ESC 键
2document.addEventListener('keydown', function(e){
3 if(e.key === 'Escape' && currentEffect) exitEffect();
4});
5
6// 2. 浏览器原生退出全屏事件
7document.addEventListener('fullscreenchange', function(){
8 if(!document.fullscreenElement && currentEffect) exitEffect();
9});
10
11// 3. 双击退出
12overlay.addEventListener('dblclick', function(){
13 if(currentEffect) exitEffect();
14});
退出时需要清理所有动画帧、定时器和音频:
1function exitEffect(){
2 animFrames.forEach(function(id){ cancelAnimationFrame(id); });
3 animFrames = [];
4 intervals.forEach(function(id){ clearInterval(id); });
5 intervals = [];
6 if(noiseNode){ try{ noiseNode.stop(); }catch(e){} noiseNode = null; }
7 // 退出浏览器全屏
8 if(document.fullscreenElement) document.exitFullscreen();
9}
14 种效果实现
1. 经典蓝屏(BSOD)
模拟 Windows 10/11 蓝屏死机界面,包含 :( 表情、错误代码、QR 码占位和进度百分比:
1var codes = ['CRITICAL_PROCESS_DIED','SYSTEM_THREAD_EXCEPTION_NOT_HANDLED',
2 'IRQL_NOT_LESS_OR_EQUAL','PAGE_FAULT_IN_NONPAGED_AREA'];
3var code = codes[Math.floor(Math.random()*codes.length)];
进度百分比每 900ms 随机递增,模拟"收集错误信息"过程。
2. Windows 更新
蓝色背景 + 旋转加载图标 + 进度条,关键细节:进度停在 97% 不再增长,模拟真实 Windows 更新卡住的体验:
1var iv = setInterval(function(){
2 if(pct < 97){
3 pct += Math.floor(Math.random()*3)+1;
4 if(pct > 97) pct = 97; // 永远到不了 100%
5 }
6 if(pct >= 97) clearInterval(iv);
7}, 500);
3. macOS 更新
黑色背景 + Apple 图标 + 白色进度条 + 剩余时间倒计时,与真实 macOS 更新界面一致。
4. 黑客帝国数字雨
这是最复杂的效果之一,需要实现慢速下落 + 尾迹渐隐。
数据模型
每列维护一个独立的"流"对象:
1streams[i] = {
2 y: Math.random() * H / fontSize * -1, // 从屏幕上方随机位置开始
3 speed: 0.3 + Math.random() * 0.5, // 慢速下落(0.3-0.8)
4 trailLen: 12 + Math.floor(Math.random() * 18) // 尾迹长度 12-30
5};
渲染流程
1function draw(){
2 // 半透明黑色覆盖,产生渐隐效果
3 ctx.fillStyle = 'rgba(0,0,0,0.06)';
4 ctx.fillRect(0, 0, W, H);
5
6 for(var i = 0; i < columns; i++){
7 var s = streams[i];
8
9 // 隔帧移动,控制速度
10 if(frameCount % 2 === 0 || s.speed > 0.6){
11 s.y += s.speed;
12 }
13
14 // 头部字符 - 白色高亮 + 绿色发光
15 ctx.fillStyle = '#fff';
16 ctx.shadowColor = '#00ff41';
17 ctx.shadowBlur = 8;
18 ctx.fillText(headCh, x, y);
19 ctx.shadowBlur = 0;
20
21 // 尾迹字符 - 从亮绿到暗绿渐隐
22 for(var t = 1; t <= s.trailLen; t++){
23 var alpha = 1 - (t / s.trailLen);
24 var green = Math.floor(180 + 75 * alpha);
25 ctx.fillStyle = 'rgba(0,' + green + ',65,' + (alpha * 0.7) + ')';
26 ctx.fillText(tch, x, ty);
27 }
28 }
29}
关键优化:
rgba(0,0,0,0.06)覆盖产生自然渐隐,上方字符逐步变黑消失- 头部白色 +
shadowBlur产生发光效果 - 尾迹从亮绿
rgba(0,255,65,0.7)渐变到暗绿rgba(0,180,65,0.05) - 每列独立速度,视觉上更自然
5. 黑客终端
逐字打字动画,支持多种颜色类型:
1var lines = [
2 {t:'prompt', v:'root@kali:~# nmap -sV -sC --script=vuln 192.168.1.0/24'},
3 {t:'output', v:'Starting Nmap 7.94 at 2026-06-03 22:15 CST'},
4 {t:'warn', v:'VULNERABLE: SQL Injection in /api/users'},
5 {t:'success',v:'Access granted. Welcome to Ubuntu 22.04 LTS'},
6 {t:'error', v:'████ SYSTEM FULLY COMPROMISED ████'},
7];
CSS 类控制颜色:.se-hacker-prompt(暗绿)、.se-hacker-output(绿色)、.se-hacker-warn(黄色)、.se-hacker-error(红色)、.se-hacker-success(青绿)。
6. 睡眠白噪音
Canvas 绘制静态噪点 + Web Audio API 生成三种噪音:
| 噪音类型 | 算法 | 听感 |
|---|---|---|
| 白噪音 | Math.random() * 2 - 1 | 嘶嘶声,均匀频谱 |
| 棕噪音 | 布朗运动:(last + 0.02 * w) / 1.02 | 低沉轰鸣 |
| 粉噪音 | Voss-McCartney 滤波器 | 自然雨声 |
1// 棕噪音生成
2var last = 0;
3for(var i = 0; i < bufferSize; i++){
4 var w = Math.random() * 2 - 1;
5 data[i] = (last + 0.02 * w) / 1.02;
6 last = data[i];
7 data[i] *= 3.5; // 增益补偿
8}
按钮切换时需要 stopPropagation() 防止触发 overlay 的退出事件。
7. FBI 锁定屏幕
DOJ 徽章 + 红色标题 + 法律条文,底部添加免责声明。
8. 碎屏效果
Canvas 递归裂纹算法:
1function drawCrack(sx, sy, angle, length, depth){
2 if(depth <= 0 || length < 5) return;
3 var ex = sx + Math.cos(angle) * length;
4 var ey = sy + Math.sin(angle) * length;
5 ctx.beginPath();
6 ctx.moveTo(sx, sy);
7 ctx.lineTo(ex, ey);
8 ctx.strokeStyle = 'rgba(255,255,255,' + (0.15 + depth * 0.08) + ')';
9 ctx.lineWidth = depth * 0.8;
10 ctx.stroke();
11 // 递归分支
12 for(var i = 0; i < Math.floor(Math.random()*3)+1; i++)
13 drawCrack(ex, ey, angle + (Math.random()-0.5)*1.2,
14 length*(0.4+Math.random()*0.4), depth-1);
15}
从中心点辐射 6-10 条主裂纹,每条裂纹递归 5 层产生分支。配合冲击波圆环和中心亮点。
9. DVD 屏保
经典弹跳 Logo,碰壁换色 + 发光效果:
1if(hit){
2 ci = (ci + 1) % colors.length;
3 logo.style.color = colors[ci];
4 logo.style.textShadow = '0 0 20px ' + colors[ci] + ',0 0 40px ' + colors[ci];
5}
6logo.style.left = x + 'px';
7logo.style.top = y + 'px';
10. CRT 显示器
三层叠加实现复古 CRT 效果:
- Canvas 层:绿色磷光 DOS 启动文字,逐行输出
- 扫描线层:CSS
repeating-linear-gradient实现 3px 间距的水平线 - 暗角层:CSS
radial-gradient模拟 CRT 边缘变暗 - 闪烁层:CSS animation 模拟微弱闪烁
1.se-crt-scanlines{
2 background: repeating-linear-gradient(
3 0deg,
4 rgba(0,0,0,.15) 0px,
5 rgba(0,0,0,.15) 1px,
6 transparent 1px,
7 transparent 3px
8 );
9}
10.se-crt-vignette{
11 background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,.7) 100%);
12}
Canvas 使用 rgba(0,10,0,0.01) 衰减模拟磷光余辉。
11. 故障效果(Glitch)
Canvas 实现赛博朋克故障艺术:
- 随机色块:红/青色半透明矩形
- 水平位移:
getImageData+putImageData实现条纹错位 - 噪点块:随机像素填充
- RGB 分离文字:同一文字用红/青色偏移绘制
1// 水平位移条纹
2var sliceY = Math.random() * H;
3var sliceH = Math.random() * 30 + 5;
4var shift = (Math.random() - 0.5) * 60;
5var imgData = ctx.getImageData(0, sliceY, W, sliceH);
6ctx.putImageData(imgData, shift, sliceY);
12. 星空穿越
3D 透视投影 + 速度拖尾:
1// 3D → 2D 投影
2var sx = (s.x / s.z) * W/2 + cx;
3var sy = (s.y / s.z) * H/2 + cy;
4var r = Math.max(0.5, (1 - s.z/W) * 3); // 近大远小
5
6// 拖尾线条
7var prevZ = s.z + speed;
8var psx = (s.x / prevZ) * W/2 + cx;
9var psy = (s.y / prevZ) * H/2 + cy;
10ctx.beginPath();
11ctx.moveTo(psx, psy);
12ctx.lineTo(sx, sy);
600 颗星从中心向外飞散,z 坐标递减模拟接近效果。
13. 火焰效果
经典 DOOM 火焰算法,基于像素传播 + 衰减:
1// 底部随机火源
2for(var x = 0; x < W; x++){
3 firePixels[(H-1)*W+x] = Math.random() > 0.4 ?
4 Math.floor(Math.random()*160)+96 : 0;
5}
6
7// 向上传播 + 衰减
8for(var y = 0; y < H-1; y++){
9 for(var x = 0; x < W; x++){
10 var src = ((y+1)*W) + ((x + Math.floor(Math.random()*3)-1+W) % W);
11 var val = firePixels[src] - Math.floor(Math.random()*3) - 1;
12 firePixels[y*W+x] = val > 0 ? val : 0;
13 }
14}
调色板从黑→红→橙→黄→白渐变,使用 4x 缩放降低计算量。
14. 假加载画面
旋转加载圈 + 进度条,永远到不了 100%:
1if(pct >= 98 && Math.random() < 0.1){
2 itemIdx++;
3 pct = 96 + Math.floor(Math.random()*2); // 回退到 96-97%
4}
循环切换 10 种加载提示文字,进度在 96-98% 之间反复跳动。
全宽布局集成
与像素画页面相同的集成方式:
1/* main.css */
2.main-wrapper:has(#screeneffects-page) {
3 padding: calc(var(--header-height) + 80px) 0 0 !important;
4}
1{{/* baseof.html */}}
2{{ $noSidebar := or ... (eq .Type "screeneffects") (eq .Type "error") }}
技术要点总结
| 技术 | 应用场景 |
|---|---|
| Fullscreen API | 全屏沉浸式体验 |
| Canvas 2D | 数字雨/碎屏/CRT/故障/星空/火焰 |
| Web Audio API | 白噪音/棕噪音/粉噪音生成 |
| CSS animation | 旋转图标/光标闪烁/CRT扫描线 |
| CSS :has() | 全宽布局选择器 |
| requestAnimationFrame | 60fps 流畅动画 |
| getImageData/putImageData | 故障效果水平位移 |
| 递归算法 | 碎屏裂纹分支 |
| 3D 透视投影 | 星空穿越 |
| 像素传播算法 | DOOM 火焰 |
留言评论
期待你的想法评论加载中