78 lines
4.6 KiB
HTML
78 lines
4.6 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover">
|
|
<title>TurboSu - 档位</title>
|
|
<style>
|
|
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
|
|
html,body{width:100%;height:100%;overflow:hidden;background:#000;font-family:'SF Pro Text',-apple-system,'PingFang SC',sans-serif}
|
|
#root{width:100%;height:100%;display:flex;align-items:center;justify-content:center}
|
|
#box{position:relative;overflow:hidden}
|
|
#box.contain{max-width:100vw;max-height:100vh}
|
|
#box.cover{min-width:100vw;min-height:100vh}
|
|
#box.fill{width:100vw;height:100vh}
|
|
#box.center{width:auto;height:auto}
|
|
#content{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#0a0a14,#1a1a30);gap:3vw}
|
|
.gear{width:min(30vw,140px);height:min(30vw,140px);border-radius:50%;background:rgba(52,130,255,.12);border:3px solid rgba(52,130,255,.45);display:flex;align-items:center;justify-content:center;box-shadow:0 0 40px rgba(52,130,255,.15)}
|
|
.gear span{font-size:min(14vw,64px);font-weight:900;color:#fff;transition:transform .15s ease}
|
|
.gear.flash span{color:#FF9F0A;transform:scale(1.1)}
|
|
.bars{display:flex;flex-direction:column;gap:4px}
|
|
.bar-item{display:flex;align-items:center;gap:6px}
|
|
.bar-item .label{font-size:min(2.5vw,13px);color:rgba(255,255,255,.35);width:min(5vw,20px);text-align:right}
|
|
.bar-item .fill{height:6px;background:rgba(255,255,255,.08);border-radius:3px;width:min(20vw,100px)}
|
|
.bar-item .fill-inner{height:100%;border-radius:3px;transition:background .3s ease}
|
|
.bar-item.active .label{color:#fff}
|
|
.bar-item.active .fill-inner{background:var(--accent,#3482FF)}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"><div id="box" class="contain"><div id="content">
|
|
<div class="gear" id="gear-el"><span id="gear-txt">1</span></div>
|
|
<div class="bars">
|
|
<div class="bar-item" data-g="1"><span class="label">1</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
<div class="bar-item" data-g="2"><span class="label">2</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
<div class="bar-item" data-g="3"><span class="label">3</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
<div class="bar-item" data-g="4"><span class="label">4</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
<div class="bar-item" data-g="5"><span class="label">5</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
<div class="bar-item" data-g="6"><span class="label">6</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
<div class="bar-item" data-g="7"><span class="label">7</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
<div class="bar-item" data-g="8"><span class="label">8</span><div class="fill"><div class="fill-inner"></div></div></div>
|
|
</div>
|
|
</div></div></div>
|
|
<script>
|
|
const M={aspect_ratio:"1:1",render_mode:"contain"};
|
|
(function(){
|
|
const box=document.getElementById('box'),gearTxt=document.getElementById('gear-txt'),gearEl=document.getElementById('gear-el');
|
|
const proto=location.protocol==='https:'?'wss:':'ws:',wsUrl=proto+'//'+location.host+'/ws';
|
|
let ws,rt,lastGear=-1;
|
|
|
|
function parseRatio(r){if(!r||r==='auto')return null;const p=r.split(':');if(p.length===2){const w=+p[0],h=+p[1];if(w>0&&h>0)return w/h;}return null;}
|
|
|
|
function apply(){
|
|
const ratio=parseRatio(M.aspect_ratio||'1:1'),mode=M.render_mode||'contain';
|
|
if(!ratio){box.style.width='100vw';box.style.height='100vh';box.className='fill';return;}
|
|
const vw=window.innerWidth,vh=window.innerHeight,vr=vw/vh;let cw,ch;
|
|
switch(mode){
|
|
case'cover':if(vr>ratio){cw=vw;ch=vw/ratio}else{ch=vh;cw=vh*ratio};box.className='cover';break;
|
|
case'fill':box.style.width='100vw';box.style.height='100vh';box.className='fill';return;
|
|
case'center':cw=Math.min(vw,800);ch=cw/ratio;if(ch>vh){ch=Math.min(vh,800);cw=ch*ratio};box.className='center';break;
|
|
default:if(vr>ratio){ch=vh;cw=vh*ratio}else{cw=vw;ch=vw/ratio};box.className='contain';
|
|
}
|
|
if(mode!=='fill'){box.style.width=cw+'px';box.style.height=ch+'px';}
|
|
}
|
|
|
|
function connect(){
|
|
ws=new WebSocket(wsUrl);ws.onopen=()=>{if(rt){clearTimeout(rt);rt=null}};
|
|
ws.onmessage=(e)=>{try{const m=JSON.parse(e.data);if(m.type==='telemetry'){const g=m.data.gear||0;if(g!==lastGear){lastGear=g;gearEl.classList.add('flash');setTimeout(()=>gearEl.classList.remove('flash'),200)}
|
|
gearTxt.textContent=g===0?'R':String(g);
|
|
document.querySelectorAll('.bar-item').forEach(el=>{el.classList.toggle('active',parseInt(el.dataset.g)===g)})}}catch(e){}};
|
|
ws.onclose=()=>{rt=setTimeout(connect,2000)};
|
|
}
|
|
|
|
apply();window.addEventListener('resize',apply);connect();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|