Files
pk10/Static/js/pk10-sound.js
T
li 4a94fe36cf feat: 重构代码架构 + 新增报表/跟单/输赢统计功能
- 拆分 HomeController → TransactionController, ReportWebController, FollowPlanController
- 新增 Service 层: TransactionService, ReportService, FollowPlanService
- pk10.php JS 抽离为 4 个独立文件 (sound/race/bet/poll)
- 前台新增报表查询页面 (/report) + 跟单计划页面 (/follow-plan)
- 后台新增跟单计划管理 + 用户输赢明细统计
- 封盘状态显示倒计时 (x:xx)
- 音效仅在开奖弹窗打开时播放
- 路由按模块分组整理
- autoload 支持 App\Services 命名空间
2026-03-27 18:41:06 +08:00

34 lines
3.1 KiB
JavaScript

/**
* PK10 Sound Engine (SoundFX)
* Depends on: (none - first to load)
* Provides: SoundFX, toggleRaceSound
*
* Sound only plays when the race modal is open (raceModalOpen === true).
* betSuccess/error are exempt — they are UI feedback, not race sounds.
*/
var SoundFX={
enabled:localStorage.getItem('raceSoundOn')!=='0',
_cache:{},
// Core play — checks both enabled AND raceModalOpen
_play:function(name,vol,startAt){if(!this.enabled||!window.raceModalOpen)return null;var a=new Audio('/Static/sounds/'+name+'.mp3');a.volume=Math.min(vol||0.6,1);if(startAt)a.currentTime=startAt;a.play().catch(function(){});return a;},
_stop:function(key){if(this._cache[key]){try{this._cache[key].pause();this._cache[key].currentTime=0;}catch(e){}delete this._cache[key];}},
engineIdle:function(){this._cache.idle=this._play('engine',0.3);},
trafficBeep:function(n){this._play('beep',n>0?0.4:0.6);},
engineStart:function(){this._stop('idle');this._cache.eng=this._play('engine',0.7);},
accelerate:function(){this._stop('eng');this._cache.race=this._play('racing',0.5);},
gearShift:function(){this._play('engine',0.25,1.5);},
whoosh:function(){this._play('whoosh',0.5);},
brakeSqueal:function(){this._stop('race');this._play('brake',0.6);},
finish:function(){this._play('finish',0.7);},
podium:function(){this._play('finish',0.5);},
// tick — race countdown sound, only when modal open
tick:function(){if(!this.enabled||!window.raceModalOpen)return;try{var ctx=new(window.AudioContext||window.webkitAudioContext)();var o=ctx.createOscillator(),g=ctx.createGain();o.type='sine';o.frequency.value=800;g.gain.setValueAtTime(0.1,ctx.currentTime);g.gain.linearRampToValueAtTime(0,ctx.currentTime+0.05);o.connect(g).connect(ctx.destination);o.start();o.stop(ctx.currentTime+0.05);}catch(e){}},
// betSuccess/error — UI feedback, always plays when enabled (no modal check)
betSuccess:function(){if(!this.enabled)return;try{var ctx=new(window.AudioContext||window.webkitAudioContext)();[880,1175].forEach(function(f,i){var o=ctx.createOscillator(),g=ctx.createGain();o.type='sine';o.frequency.value=f;g.gain.setValueAtTime(0.08,ctx.currentTime+i*0.08);g.gain.linearRampToValueAtTime(0,ctx.currentTime+i*0.08+0.12);o.connect(g).connect(ctx.destination);o.start(ctx.currentTime+i*0.08);o.stop(ctx.currentTime+i*0.08+0.12);});}catch(e){}},
error:function(){if(!this.enabled)return;try{var ctx=new(window.AudioContext||window.webkitAudioContext)();var o=ctx.createOscillator(),g=ctx.createGain();o.type='square';o.frequency.setValueAtTime(200,ctx.currentTime);o.frequency.setValueAtTime(150,ctx.currentTime+0.15);g.gain.setValueAtTime(0.06,ctx.currentTime);g.gain.linearRampToValueAtTime(0,ctx.currentTime+0.3);o.connect(g).connect(ctx.destination);o.start();o.stop(ctx.currentTime+0.3);}catch(e){}},
// stopAll — kill all cached sounds (called when modal closes)
stopAll:function(){for(var k in this._cache){this._stop(k);}},
toggle:function(){this.enabled=!this.enabled;localStorage.setItem('raceSoundOn',this.enabled?'1':'0');return this.enabled;}
};
function toggleRaceSound(){var on=SoundFX.toggle();var btn=document.querySelector('.rs-sound');if(btn)btn.textContent=on?'🔊':'🔇';if(on)SoundFX.tick();}