fix: composite dashboard RPM gauge - ╭ shape (left 90° arc + top straight line)
This commit is contained in:
@@ -48,7 +48,7 @@ html,body{width:100%;height:100%;overflow:hidden;background:#0a0a0f;color:#fff;
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"><div id="box" class="contain"><div id="content">
|
||||
<div id="rpm-area"><svg id="rpm-svg" viewBox="0 0 640 440" preserveAspectRatio="xMidYMid meet"></svg></div>
|
||||
<div id="rpm-area"><svg id="rpm-svg" viewBox="0 0 640 480" preserveAspectRatio="xMidYMid meet"></svg></div>
|
||||
<div id="speed-area"><div id="speed-val">0</div><div id="speed-unit">KM/H</div></div>
|
||||
<div id="gear-area"><div id="gear-box"><span id="gear-val">1</span></div></div>
|
||||
<div id="pedals">
|
||||
@@ -74,10 +74,62 @@ let GAME_ID='';
|
||||
const proto=location.protocol==='https:'?'wss:':'ws:',wsUrl=proto+'//'+location.host+'/ws';
|
||||
let ws,rt,lR=-1,lG=-999;
|
||||
|
||||
const CX=320,CY=380,R=290,SA=-210,EA=-30;
|
||||
const CX=300,CY=420,R=300,SA=180,EA=270;
|
||||
|
||||
function d2r(d){return d*Math.PI/180}
|
||||
function arcP(s,e,r){const sa=d2r(s),ea=d2r(e);return`M${CX+r*Math.cos(sa)} ${CY+r*Math.sin(sa)} A${r} ${r} 0 ${e-s>180?1:0} 1 ${CX+r*Math.cos(ea)} ${CY+r*Math.sin(ea)}`}
|
||||
|
||||
function drawRpm(cur,max,gear){
|
||||
max=max||8000;cur=cur||0;
|
||||
const sx=CX+R*Math.cos(d2r(SA)),sy=CY+R*Math.sin(d2r(SA)); // arc start
|
||||
const ex=CX+R*Math.cos(d2r(EA)),ey=CY+R*Math.sin(d2r(EA)); // arc end -> top of arc
|
||||
const lx=620,ly=ey; // straight line end (right side, same Y as arc top)
|
||||
|
||||
// arc length (90°) = 2πR * 90/360
|
||||
const arcLen=Math.PI*R*90/360;
|
||||
const lineLen=lx-ex;
|
||||
const totalLen=arcLen+lineLen;
|
||||
|
||||
let h='';
|
||||
// background: arc + line
|
||||
h+=`<path d="M${sx},${sy} A${R},${R} 0 0 0 ${ex},${ey} L${lx},${ly}" fill="none" stroke="rgba(255,255,255,.04)" stroke-width="22" stroke-linecap="round" stroke-linejoin="round"/>`;
|
||||
|
||||
// scale marks on arc
|
||||
for(let r=0;r<=max;r+=1000){
|
||||
const pct=r/max;
|
||||
const ang=SA+(EA-SA)*pct;
|
||||
if(ang<=EA){
|
||||
const a=d2r(ang);
|
||||
const x1=CX+(R-18)*Math.cos(a),y1=CY+(R-18)*Math.sin(a);
|
||||
const x2=CX+(R+18)*Math.cos(a),y2=CY+(R+18)*Math.sin(a);
|
||||
const c=r>=max*0.85?'rgba(255,255,255,.12)':(r%2000===0?'rgba(255,255,255,.15)':'rgba(255,255,255,.05)');
|
||||
h+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${c}" stroke-width="${r%2000===0?2:1}"/>`;
|
||||
if(r%2000===0){
|
||||
h+=`<text x="${CX+(R-34)*Math.cos(a)}" y="${CY+(R-34)*Math.sin(a)+4}" text-anchor="middle" font-size="10" fill="rgba(255,255,255,.2)">${r/1000}</text>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// progress
|
||||
if(cur>0){
|
||||
const pct=Math.min(cur/max,1);
|
||||
if(pct<=arcLen/totalLen){
|
||||
// progress within arc
|
||||
const ap=pct*totalLen/arcLen;
|
||||
const ang=SA+(EA-SA)*ap;
|
||||
const px=CX+R*Math.cos(d2r(ang)),py=CY+R*Math.sin(d2r(ang));
|
||||
h+=`<path d="M${sx},${sy} A${R},${R} 0 0 0 ${px},${py}" fill="none" stroke="#fff" stroke-width="22" stroke-linecap="round"/>`;
|
||||
}else{
|
||||
// full arc + partial line
|
||||
const lp=(pct*totalLen-arcLen)/lineLen;
|
||||
const px=ex+lp*(lx-ex);
|
||||
h+=`<path d="M${sx},${sy} A${R},${R} 0 0 0 ${ex},${ey} L${px},${ly}" fill="none" stroke="#fff" stroke-width="22" stroke-linecap="round" stroke-linejoin="round"/>`;
|
||||
}
|
||||
}
|
||||
|
||||
h+=`<text x="${CX}" y="${CY-24}" text-anchor="middle" font-size="22" font-weight="200" fill="#fff">${Math.round(cur).toLocaleString()}</text>`;
|
||||
h+=`<text x="${CX}" y="${CY+4}" text-anchor="middle" font-size="10" fill="rgba(255,255,255,.15)">×1000 rpm</text>`;
|
||||
svg.innerHTML=h;
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
@@ -90,28 +142,6 @@ let GAME_ID='';
|
||||
box.style.width=cw+'px';box.style.height=ch+'px'
|
||||
}
|
||||
|
||||
function drawRpm(cur,max,gear){
|
||||
max=max||8000;cur=cur||0;
|
||||
const ta=EA-SA,rd=max*0.85;
|
||||
let h='';
|
||||
h+=`<path d="${arcP(SA,EA,R)}" fill="none" stroke="rgba(255,255,255,.04)" stroke-width="22" stroke-linecap="round"/>`;
|
||||
for(let r=0;r<=max;r+=1000){
|
||||
const pct=r/max,ang=SA+ta*pct,a=d2r(ang);
|
||||
const x1=CX+(R-20)*Math.cos(a),y1=CY+(R-20)*Math.sin(a);
|
||||
const x2=CX+(R+20)*Math.cos(a),y2=CY+(R+20)*Math.sin(a);
|
||||
const c=r>=rd?'rgba(255,255,255,.08)':(r%2000===0?'rgba(255,255,255,.12)':'rgba(255,255,255,.04)');
|
||||
h+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${c}" stroke-width="${r%2000===0?2:1}"/>`;
|
||||
if(r%2000===0)h+=`<text x="${CX+(R-36)*Math.cos(a)}" y="${CY+(R-36)*Math.sin(a)+4}" text-anchor="middle" font-size="10" fill="rgba(255,255,255,.2)">${r/1000}</text>`;
|
||||
}
|
||||
if(cur>0){
|
||||
const pct=Math.min(cur/max,1),eA=SA+ta*pct;
|
||||
h+=`<path d="${arcP(SA,eA,R)}" fill="none" stroke="#fff" stroke-width="22" stroke-linecap="round"/>`;
|
||||
}
|
||||
h+=`<text x="${CX}" y="${CY-12}" text-anchor="middle" font-size="24" font-weight="200" fill="#fff">${Math.round(cur).toLocaleString()}</text>`;
|
||||
h+=`<text x="${CX}" y="${CY+14}" text-anchor="middle" font-size="11" fill="rgba(255,255,255,.2)">×1000 rpm</text>`;
|
||||
svg.innerHTML=h;
|
||||
}
|
||||
|
||||
function connect(){
|
||||
ws=new WebSocket(wsUrl);ws.onopen=()=>{if(rt){clearTimeout(rt);rt=null}}
|
||||
ws.onmessage=(e)=>{try{const m=JSON.parse(e.data);
|
||||
|
||||
Reference in New Issue
Block a user