63 lines
3.1 KiB
HTML
63 lines
3.1 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:#0a0a0f;color:#fff;
|
|
font-family:'SF Pro Display',-apple-system,system-ui,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}
|
|
#content{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center}
|
|
svg{width:min(92%,480px)}
|
|
.rpm-num{font-size:min(9vw,52px);font-weight:200;fill:#fff;letter-spacing:-.02em}
|
|
.rpm-label{font-size:min(2vw,12px);fill:rgba(255,255,255,.25);letter-spacing:.15em}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="root"><div id="box" class="contain"><div id="content">
|
|
<svg viewBox="0 0 320 200">
|
|
<path d="M 20,168 A 140,140 0 0,1 300,168" fill="none" stroke="rgba(255,255,255,.06)" stroke-width="16" stroke-linecap="round"/>
|
|
<path id="arc" d="M 20,168 A 140,140 0 0,1 300,168" fill="none" stroke="#fff" stroke-width="16" stroke-linecap="round" stroke-dasharray="440" stroke-dashoffset="440" opacity=".9"/>
|
|
<text x="160" y="148" text-anchor="middle" class="rpm-num" id="rpm-v">0</text>
|
|
<text x="160" y="172" text-anchor="middle" class="rpm-label">RPM</text>
|
|
<g class="rpm-label" style="font-size:9px">
|
|
<text x="18" y="188">0</text><text x="88" y="70">2k</text><text x="160" y="44">4k</text><text x="232" y="70">6k</text><text x="302" y="188">8k</text>
|
|
</g>
|
|
</svg>
|
|
</div></div></div>
|
|
<script>
|
|
const M={aspect_ratio:"16:9",render_mode:"contain"};
|
|
(function(){
|
|
const box=document.getElementById('box'),arc=document.getElementById('arc'),rpmV=document.getElementById('rpm-v');
|
|
const proto=location.protocol==='https:'?'wss:':'ws:',wsUrl=proto+'//'+location.host+'/ws';
|
|
let ws,rt;
|
|
|
|
function parseRatio(r){if(!r||r==='auto')return null;const p=r.split(':');if(p.length===2){const w=+p[0],h=+p[1];return w/h}return null}
|
|
|
|
function apply(){
|
|
const ratio=parseRatio(M.aspect_ratio||'16:9'),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;
|
|
if(mode==='cover'){cw=vw;ch=vw/ratio;box.className='cover'}
|
|
else{box.className='contain';cw=vr>ratio?vh*ratio:vw;ch=vr>ratio?vh:vw/ratio}
|
|
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 d=m.data,rpm=d.rpm||0,max=d.max_rpm||8000,pct=Math.min(rpm/max,1);rpmV.textContent=rpm.toFixed(0);arc.setAttribute('stroke-dashoffset',440-440*pct)}}catch(e){}}
|
|
ws.onclose=()=>{rt=setTimeout(connect,2000)}
|
|
}
|
|
|
|
apply();window.addEventListener('resize',apply);connect()
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|