import { groupChat } from './group-chat/Index.ts';
/**
* 彩蛋模块。
* 处理版本号点击触发的破碎效果和群聊彩蛋。
*/
export class EasterEgg {
private _clickCount: number = 0;
private _lastClickTime: number = 0;
private readonly _versionElement: HTMLElement | null;
private _audioCtx: AudioContext | null = null;
private _restoreTimer: ReturnType<typeof setTimeout> | null = null;
public constructor() {
this._versionElement = document.getElementById('app-version');
if (this._versionElement) {
this.init();
}
}
/**
* 初始化彩蛋功能。
*/
private init(): void {
if (!this._versionElement) {
return;
}
this._versionElement.addEventListener('click', (e: MouseEvent) => {
this.handleClick(e);
});
this._versionElement.style.cursor = 'pointer';
this._versionElement.style.userSelect = 'none';
this._versionElement.title = '版本号';
}
/**
* 处理版本号点击事件。
*/
private handleClick(e: MouseEvent): void {
const now = Date.now();
/*
* 如果距离上次点击超过 2 秒,则重置计数器
*/
if (now - this._lastClickTime > 2000) {
this._clickCount = 0;
}
this._lastClickTime = now;
this._clickCount++;
this.animateClick();
if (this._clickCount === 5) {
this.triggerShakeEffect();
} else if (this._clickCount > 5 && this._clickCount < 10) {
/*
* 如果在破碎状态下继续点击,则延长自动恢复时间
*/
if (document.body.classList.contains('easter-egg--broken')) {
this.resetAutoRestoreTimer();
}
} else if (this._clickCount >= 10) {
this.triggerGroupChat();
}
}
/**
* 播放点击动画效果。
*/
private animateClick(): void {
if (!this._versionElement) {
return;
}
this._versionElement.style.transition = 'transform 0.1s';
this._versionElement.style.transform = 'scale(0.9)';
window.setTimeout(() => {
if (this._versionElement) {
this._versionElement.style.transform = 'scale(1)';
}
}, 100);
}
/**
* 触发震动破碎效果。
*/
private triggerShakeEffect(): void {
this.resetEffects();
/*
* 创建石头元素(动画由 CSS 控制)
*/
const stone = document.createElement('div');
stone.classList.add('easter-egg-stone');
stone.innerText = '🪨';
document.body.appendChild(stone);
/*
* 0.6 秒后石头飞行完毕,触发碰撞效果
*/
window.setTimeout(() => {
stone.remove();
this.createImpactFlash();
this.playBreakSound();
document.body.classList.add('easter-egg--shake');
document.body.classList.add('easter-egg--broken');
/*
* 500 毫秒后停止震动
*/
window.setTimeout(() => {
document.body.classList.remove('easter-egg--shake');
}, 500);
this.resetAutoRestoreTimer();
}, 600);
}
/**
* 创建撞击闪白效果。
*/
private createImpactFlash(): void {
const flash = document.createElement('div');
flash.classList.add('easter-egg-flash');
document.body.appendChild(flash);
/*
* 动画结束后移除闪白层
*/
flash.addEventListener('animationend', () => {
flash.remove();
});
}
/**
* 重置自动恢复定时器。
*/
private resetAutoRestoreTimer(): void {
if (this._restoreTimer) {
clearTimeout(this._restoreTimer);
}
/*
* 随机 5-10 秒后自动恢复
*/
const delay = 5000 + Math.random() * 5000;
this._restoreTimer = window.setTimeout(() => {
this.resetEffects();
}, delay);
}
/**
* 重置所有效果。
*/
private resetEffects(): void {
document.body.classList.remove('easter-egg--shake');
document.body.classList.remove('easter-egg--broken');
if (this._restoreTimer) {
clearTimeout(this._restoreTimer);
}
}
/**
* 触发群聊彩蛋。
*/
private triggerGroupChat(): void {
this.resetEffects();
groupChat.open();
this._clickCount = 0;
}
/**
* 播放玻璃破碎音效。
* 使用 Web Audio API 合成破碎音效。
*/
private playBreakSound(): void {
try {
if (!this._audioCtx) {
const AudioContextClass = window.AudioContext || (window as any).webkitAudioContext;
this._audioCtx = new AudioContextClass();
}
/*
* 如果音频上下文处于挂起状态(浏览器策略),尝试恢复
*/
if (this._audioCtx.state === 'suspended') {
void this._audioCtx.resume();
}
const ctx = this._audioCtx;
/*
* 创建白噪音缓冲区(2 秒)
*/
const bufferSize = ctx.sampleRate * 2.0;
const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1;
}
/*
* 创建噪音源
*/
const noise = ctx.createBufferSource();
noise.buffer = buffer;
/*
* 高通滤波模拟玻璃的高频声
*/
const filter = ctx.createBiquadFilter();
filter.type = 'highpass';
filter.frequency.value = 800;
filter.Q.value = 10;
/*
* 包络:控制音量变化模拟撞击和余音
*/
const gain = ctx.createGain();
noise.connect(filter);
filter.connect(gain);
gain.connect(ctx.destination);
const now = ctx.currentTime;
/*
* 冲击:瞬间大音量
*/
gain.gain.setValueAtTime(0, now);
gain.gain.linearRampToValueAtTime(0.8, now + 0.01);
/*
* 衰减:迅速下降
*/
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.3);
noise.start(now);
noise.stop(now + 0.3);
} catch (e) {
console.warn('Easter egg sound synthesis failed:', e);
}
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
这是一个非常有趣的彩蛋模块,实现了一个交互式的版本号点击效果。让我详细解释一下这个代码的功能和实现:
主要功能
这个模块监听页面中版本号元素的点击事件,根据不同的点击次数触发不同的彩蛋效果:
1. 点击计数机制
private handleClick(e: MouseEvent): void {
const now = Date.now();
// 如果距离上次点击超过 2 秒,则重置计数器
if (now - this._lastClickTime > 2000) {
this._clickCount = 0;
}
this._lastClickTime = now;
this._clickCount++;
// ...
}
- 在2秒内的连续点击才会累计计数
- 超过2秒间隔会重置计数器
2. 不同阶段的效果
阶段1:基础点击动画(每次点击)
private animateClick(): void {
this._versionElement.style.transform = 'scale(0.9)';
// 100ms后恢复原始大小
}
- 每次点击都有一个缩放动画效果
阶段2:破碎效果(第5次点击)
private triggerShakeEffect(): void {
// 1. 创建石头元素飞行动画
const stone = document.createElement('div');
stone.classList.add('easter-egg-stone');
stone.innerText = '🪨';
// 2. 600ms后触发撞击效果
setTimeout(() => {
this.createImpactFlash(); // 闪白效果
this.playBreakSound(); // 破碎音效
document.body.classList.add('easter-egg--shake'); // 震动
document.body.classList.add('easter-egg--broken'); // 破碎状态
}, 600);
}
阶段3:群聊彩蛋(第10次点击)
private triggerGroupChat(): void {
this.resetEffects();
groupChat.open(); // 打开群聊功能
this._clickCount = 0;
}
3. 音效合成
最有趣的部分是使用 Web Audio API 实时合成玻璃破碎音效:
private playBreakSound(): void {
// 创建白噪音
const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = Math.random() * 2 - 1; // 生成随机噪音
}
// 高通滤波模拟玻璃高频声
const filter = ctx.createBiquadFilter();
filter.type = 'highpass';
filter.frequency.value = 800;
// 音量包络:冲击 → 衰减
gain.gain.setValueAtTime(0, now);
gain.gain.linearRampToValueAtTime(0.8, now + 0.01); // 瞬间冲击
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.3); // 快速衰减
}
4. 自动恢复机制
private resetAutoRestoreTimer(): void {
// 随机 5-10 秒后自动恢复正常状态
const delay = 5000 + Math.random() * 5000;
this._restoreTimer = setTimeout(() => {
this.resetEffects();
}, delay);
}
设计亮点
- 渐进式交互:从简单点击动画到复杂的破碎效果,再到隐藏功能
- 视听结合:视觉效果(震动、闪白、破碎)+ 音效合成
- 用户体验:自动恢复机制避免永久破坏界面
- 技术展示:Web Audio API 的创意使用,实时合成音效而非播放预录音频
这是一个很好的用户体验设计案例,既有趣又不会干扰正常使用。
AI 正在分析代码…
评论加载中...