Files
fenix8v3-watchface/preview.html
ericwyuan 553c55026a Initial commit: Fenix 8 V3 data watch face
- Restore watchface-kit design (500x500, scaled per device)
- 7 themes auto-generated from tokens via gen_themes.py -> Themes.mc
- Four-segment gradient progress ring (22+21 ticks, 4 anchor dots)
- Center time, date band w/ Bluetooth icon, top battery + dual complications, bottom distance/steps
- 17 devices compiled clean (SDK 9.1.0)
- Includes browser/JS preview (preview.html) + 7-theme SVG sheet
2026-09-09 14:40:33 +08:00

323 lines
18 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Fenix 8 V3 表盘运行预览</title>
<style>
:root{ --bg:#0a0a0a; --panel:#141413; --ink:#e9e6e0; --dim:#8d8a84; --line:#26261f; --amber:#FEAA00; }
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.6 -apple-system,"PingFang SC","Microsoft YaHei",Helvetica,Arial,sans-serif}
.wrap{max-width:1080px;margin:0 auto;padding:32px 20px 64px}
h1{font-size:22px;margin:0 0 4px}
.lede{color:var(--dim);margin:0 0 24px}
.stage{display:flex;gap:28px;flex-wrap:wrap;align-items:flex-start}
.facebox{background:#000;border-radius:14px;padding:14px;box-shadow:0 8px 30px #0008}
canvas{display:block;background:#000;border-radius:50%}
canvas.square{border-radius:12px}
.controls{display:flex;flex-direction:column;gap:18px;min-width:240px;flex:1}
.card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 16px}
.card h3{margin:0 0 10px;font-size:13px;color:var(--dim);font-weight:500}
.themes{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
.themes button{cursor:pointer;border:1px solid var(--line);background:#1c1c19;color:var(--ink);border-radius:8px;padding:8px 4px;font-size:12px;transition:.15s}
.themes button:hover{border-color:var(--amber)}
.themes button.active{background:var(--amber);color:#000;border-color:var(--amber);font-weight:600}
.row{display:flex;align-items:center;gap:10px;margin:8px 0}
.row label{flex:1;color:var(--dim)}
input[type=range]{width:130px}
.chk{display:flex;align-items:center;gap:8px;color:var(--dim)}
.val{font:12px ui-monospace,Menlo,monospace;color:var(--amber);min-width:42px;text-align:right}
.gallery{margin-top:34px}
.gallery h2{font-size:16px;border-bottom:1px solid var(--line);padding-bottom:8px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:18px;margin-top:18px}
figure{margin:0;text-align:center}
figure canvas{margin:0 auto;border-radius:50%;background:#000}
figcaption{color:var(--dim);font-size:12px;margin-top:8px}
.note{color:var(--dim);font-size:12px;margin-top:6px}
</style>
</head>
<body>
<div class="wrap">
<h1>Fenix 8 V3 · 运行效果预览</h1>
<p class="lede">网页版 1:1 还原 <code>source/Fenix8V3View.mc</code><code>onUpdate</code> 绘制逻辑:同坐标、同主题色、同指标填充。下方可切换 7 套主题、调指标进度、套圆形遮罩。</p>
<div class="stage">
<div class="facebox"><canvas id="face" width="500" height="500"></canvas></div>
<div class="controls">
<div class="card">
<h3>主题 Theme</h3>
<div class="themes" id="themes"></div>
</div>
<div class="card">
<h3>指标进度(驱动进度环)</h3>
<div class="row"><label>步数 Steps</label><input type="range" id="m0" min="0" max="100" value="84"><span class="val" id="v0"></span></div>
<div class="row"><label>卡路里 Calories</label><input type="range" id="m1" min="0" max="100" value="64"><span class="val" id="v1"></span></div>
<div class="row"><label>距离 Distance</label><input type="range" id="m2" min="0" max="100" value="65"><span class="val" id="v2"></span></div>
<div class="row"><label>楼层 Floors</label><input type="range" id="m3" min="0" max="100" value="80"><span class="val" id="v3"></span></div>
<div class="row"><label>动动条 Move Bar</label><input type="range" id="m4" min="0" max="100" value="40"><span class="val" id="v4"></span></div>
<div class="note">默认值对应设计稿:顶右=步数 · 顶左=卡路里 · 底右=动动条 · 底左=楼层</div>
</div>
<div class="card">
<h3>显示 / 外观</h3>
<div class="row"><label>电量 Battery</label><input type="range" id="bat" min="0" max="100" value="78"><span class="val" id="vbat"></span></div>
<div class="row"><label>天气温度 °C</label><input type="range" id="temp" min="-10" max="45" value="23"><span class="val" id="vtemp"></span></div>
<div class="chk"><input type="checkbox" id="round" checked><label for="round">圆形设备遮罩(多数 Fenix/Epix 为圆屏)</label></div>
<div class="chk"><input type="checkbox" id="live" checked><label for="live">时间实时走动</label></div>
</div>
</div>
</div>
<div class="gallery">
<h2>七套主题一览(实时当前时间)</h2>
<div class="grid" id="gallery"></div>
</div>
</div>
<script>
// ---- 主题数据(与 source/Themes.mc 一致,由 watchface-tokens.json 生成)----
const THEMES = {
"01":{name:"Ember",ringTop:[[8,0xFFF203],[15,0xFDDB0D],[25,0xFFC203],[35,0xFFA00A],[45,0xFF8705],[55,0xFF610C],[65,0xFF4904],[75,0xFF2602],[86,0xFF2604]],
ringBottom:[[7,0xAE1706],[14,0xB32504],[21,0xC03B03],[28,0xCD6206],[35,0xD57805],[42,0xDE9208],[47,0xC98D00]],
tickOff:0x545655,anchorDot:0x525551,bandFill:0xFEAA00,bandText:0x000002,accent:0xFCA901,textPrimary:0xFAFAFA,colon:0x525551,hours:0xFBFBFB,minutes:0xFCA901},
"02":{name:"Aurora",ringTop:[[8,0xA5FA11],[15,0x9EFA23],[25,0x97F84B],[35,0x8CF965],[45,0x89F87F],[55,0x74FB9F],[65,0x6DFCC6],[75,0x64FCDF],[86,0x57FDFF]],
ringBottom:[[7,0x55FDFF],[14,0x64FCDF],[21,0x6FFCC6],[28,0x74FBAE],[35,0x7EFA85],[42,0x88F96D],[47,0x81E654]],
tickOff:0x545655,anchorDot:0x0F3C82,bandFill:0x525551,bandText:0xFFFFFF,accent:0xABA9AC,textPrimary:0xFAFAFA,colon:0x525551,hours:0xFBFBFB,minutes:0xAAF701},
"03":{name:"Brass",ringTop:[[8,0xB4814F],[15,0xB88954],[25,0xC5975C],[35,0xC59E65],[45,0xCBA775],[55,0xD7AE7F],[65,0xDEBC84],[75,0xE7C48D],[86,0xF0D097]],
ringBottom:[[7,0xB97E4A],[14,0xB68954],[21,0xBD925E],[28,0xC69B67],[35,0xCEA26C],[42,0xD9AB74],[47,0xC59E70]],
tickOff:0x3A2815,anchorDot:0x62441E,bandFill:0x392810,bandText:0xFFFFFF,accent:0xB37C4A,textPrimary:0xEBCB93,colon:0x525551,hours:0xFBFBFB,minutes:0xECCC93},
"04":{name:"Voltage",ringTop:[[8,0x5CAFF0],[15,0x6EB6E0],[25,0x7FBEC5],[35,0x96CA9D],[45,0xAAD87F],[55,0xBFDF66],[65,0xD6EA40],[75,0xE8F227],[86,0xFFFB0A]],
ringBottom:[[7,0xF7FA11],[14,0xE9F225],[21,0xD6EB3F],[28,0xC0DE65],[35,0xADD880],[42,0xA0CF98],[47,0x88B98E]],
tickOff:0x545655,anchorDot:0x53AAFF,bandFill:0x53AAFF,bandText:0x000000,accent:0xABA9AC,textPrimary:0x51A7FA,colon:0x525551,hours:0xFBFBFB,minutes:0x52A8FD},
"05":{name:"Reef",ringTop:[[8,0xF87E0D],[15,0xE78E25],[25,0xCE9B4D],[35,0xBDAC65],[45,0xA5BE88],[55,0x95CA9E],[65,0x7EDEC7],[75,0x6DEBE1],[86,0x57FDFF]],
ringBottom:[[7,0xFF7905],[14,0xE78B29],[21,0xD59C3E],[28,0xBEAC63],[35,0xADB77F],[42,0xA0C796],[47,0x88B98E]],
tickOff:0x545655,anchorDot:0xFE7900,bandFill:0x52FDFF,bandText:0x000000,accent:0xFCA901,textPrimary:0xFAFAFA,colon:0x525551,hours:0xFBFBFB,minutes:0xFC7801},
"06":{name:"Acid",ringTop:[[8,0x9CCC14],[15,0xA9CE0C],[25,0xA4D610],[35,0xACDB08],[45,0xB4E107],[55,0xB5E603],[65,0xBFEE04],[75,0xBDF108],[86,0xC7F808]],
ringBottom:[[7,0xC7FA02],[14,0xBEF20A],[21,0xC0ED04],[28,0xB4E905],[35,0xB4E107],[42,0xADDD0C],[47,0x9BC603]],
tickOff:0x545655,anchorDot:0xF58909,bandFill:0x525551,bandText:0x000000,accent:0xC4F801,textPrimary:0xFAFAFA,colon:0x525551,hours:0xDBF731,minutes:0xC4F900},
"07":{name:"Kelp",ringTop:[[8,0xEF8917],[15,0xD8901C],[25,0xBD9A34],[35,0xAD9E3C],[45,0x96A54E],[55,0x7DAE5D],[65,0x61B474],[75,0x4EBB84],[86,0x36C294]],
ringBottom:[[7,0xCE520F],[14,0xB1631D],[21,0xA26A2F],[28,0x9D763E],[35,0x85874B],[42,0x76915D],[47,0x5E8651]],
tickOff:0x545655,anchorDot:0xF58909,bandFill:0x525551,bandText:0x000000,accent:0x31C192,textPrimary:0xFAFAFA,colon:0x525551,hours:0xF28708,minutes:0xF4880A}
};
const ORDER = ["01","02","03","04","05","06","07"];
// ---- 颜色工具 ----
const toHex = c => '#' + (c & 0xffffff).toString(16).padStart(6,'0');
function lerpColor(c1,c2,f){
const r1=(c1>>16)&255,g1=(c1>>8)&255,b1=c1&255;
const r2=(c2>>16)&255,g2=(c2>>8)&255,b2=c2&255;
return (Math.round(r1+(r2-r1)*f)<<16)|(Math.round(g1+(g2-g1)*f)<<8)|Math.round(b1+(b2-b1)*f);
}
function lerpStops(stops,t){
const n=stops.length; if(!n) return 0x888888;
const first=stops[0], last=stops[n-1];
if(t<=first[0]) return first[1];
if(t>=last[0]) return last[1];
for(let i=0;i<n-1;i++){const a=stops[i],b=stops[i+1];
if(t>=a[0]&&t<=b[0]) return lerpColor(a[1],b[1],(t-a[0])/(b[0]-a[0]));}
return last[1];
}
// ---- 几何常量(设计稿 500 基准)----
const TOP=[], BOT=[];
for(let a=2;a<=86;a+=4) TOP.push(a);
for(let a=6;a<=66;a+=3) BOT.push(a);
const DOW=["SUN","MON","TUE","WED","THU","FRI","SAT"];
const MON=["JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"];
// ---- 绘制原语(与 Monkey C 对应)----
function drawTick(ctx,cx,cy,rIn,rOut,w,ang,col,mirror){
const eff=mirror?-ang:ang, rad=eff*Math.PI/180, s=Math.sin(rad), c=Math.cos(rad);
ctx.strokeStyle=toHex(col); ctx.lineWidth=w; ctx.lineCap='round';
ctx.beginPath();
ctx.moveTo(cx+s*rIn, cy-c*rIn); ctx.lineTo(cx+s*rOut, cy-c*rOut); ctx.stroke();
}
function drawArcTop(ctx,cx,cy,rIn,rOut,w,angles,stops,off,mirror,lit,anchorOuter){
const n=angles.length;
for(let i=0;i<n;i++){const a=angles[i];
const on=anchorOuter?(i>=n-lit):(i<lit);
drawTick(ctx,cx,cy,rIn,rOut,w,a,on?lerpStops(stops,a):off,mirror);}
}
function drawArcBottom(ctx,cx,cy,rIn,rOut,w,botAngles,stops,off,mirror,lit){
const n=botAngles.length;
for(let i=0;i<n;i++){const a=botAngles[i];
const ang=180-a;
drawTick(ctx,cx,cy,rIn,rOut,w,ang,i<lit?lerpStops(stops,a):off,mirror);}
}
function drawRing(ctx,cx,cy,s,theme,pTR,pTL,pBR,pBL){
const rOut=249.5*s, rIn=231.5*s, tickOff=theme.tickOff;
const topStops=theme.ringTop, botStops=theme.ringBottom;
const topW=13*s, botW=9*s;
const litTR=Math.round(pTR*22), litTL=Math.round(pTL*22), litBR=Math.round(pBR*21), litBL=Math.round(pBL*21);
drawArcTop(ctx,cx,cy,rIn,rOut,topW,TOP,topStops,tickOff,false,litTR,true);
drawArcTop(ctx,cx,cy,rIn,rOut,topW,TOP,topStops,tickOff,true ,litTL,true);
drawArcBottom(ctx,cx,cy,rIn,rOut,botW,BOT,botStops,tickOff,true ,litBL);
drawArcBottom(ctx,cx,cy,rIn,rOut,botW,BOT,botStops,tickOff,false,litBR);
// 锚点圆点
const dotR=9.5*s, dotRad=240.5*s, dotCol=theme.anchorDot;
ctx.fillStyle=toHex(dotCol);
[0,93.3,180,266.7].forEach(da=>{const rad=da*Math.PI/180;
ctx.beginPath(); ctx.arc(cx+Math.sin(rad)*dotRad, cy-Math.cos(rad)*dotRad, dotR, 0, 7); ctx.fill();});
}
function drawBattery(ctx,cx,s,theme,pct){
const y=53.5*s, gw=30*s, gh=16*s, gx=cx-52*s, gy=y-gh/2;
ctx.strokeStyle=toHex(theme.accent); ctx.lineWidth=2;
ctx.strokeRect(gx,gy,gw,gh);
ctx.fillStyle=toHex(theme.accent);
ctx.fillRect(gx+gw, gy+gh*0.28, 3*s, gh*0.44);
ctx.fillStyle=toHex(theme.accent);
ctx.fillRect(gx+2, gy+2, (gw-4)*(pct/100), gh-4);
ctx.fillStyle=toHex(theme.textPrimary);
ctx.font=`${Math.round(16*s)}px -apple-system,Helvetica,Arial`;
ctx.textAlign='left'; ctx.textBaseline='middle';
ctx.fillText(Math.round(pct)+'%', cx+16*s, y);
}
function drawComplicationIcon(ctx,x,y,r,col,typeIdx){
ctx.strokeStyle=toHex(col); ctx.fillStyle=toHex(col); ctx.lineWidth=2;
if(typeIdx===0){ // sun
ctx.beginPath(); ctx.arc(x,y,r*0.45,0,7); ctx.fill();
for(let i=0;i<8;i++){const rad=i*45*Math.PI/180;
ctx.beginPath();
ctx.moveTo(x+Math.sin(rad)*r*0.6, y-Math.cos(rad)*r*0.6);
ctx.lineTo(x+Math.sin(rad)*r, y-Math.cos(rad)*r); ctx.stroke();}
} else if(typeIdx===1){ // flame
ctx.beginPath(); ctx.moveTo(x,y-r); ctx.lineTo(x+r*0.8,y+r*0.6); ctx.lineTo(x-r*0.8,y+r*0.6); ctx.closePath(); ctx.fill();
} else if(typeIdx===2){ // steps
ctx.beginPath(); ctx.moveTo(x-r*0.5,y-r*0.3); ctx.lineTo(x+r*0.5,y-r*0.3);
ctx.moveTo(x-r*0.3,y); ctx.lineTo(x+r*0.6,y); ctx.stroke();
} else if(typeIdx===3){ // distance dot
ctx.beginPath(); ctx.arc(x,y,r*0.5,0,7); ctx.fill();
} else if(typeIdx===4){ // floors chevron
ctx.beginPath(); ctx.moveTo(x,y-r); ctx.lineTo(x+r*0.7,y+r*0.2); ctx.lineTo(x-r*0.7,y+r*0.2); ctx.closePath(); ctx.fill();
} else { // battery
ctx.strokeRect(x-r*0.6,y-r*0.4,r*1.2,r*0.8);
}
}
function drawTopComplication(ctx,cx,cy,s,theme,typeIdx,isLeft,valStr){
const sideX=(isLeft?156:343)*s, iconY=90*s, valY=130.5*s;
ctx.fillStyle=toHex(theme.textPrimary);
ctx.font=`${Math.round(22*s)}px -apple-system,Helvetica,Arial`;
ctx.textAlign='center'; ctx.textBaseline='middle';
ctx.fillText(valStr, sideX, valY);
drawComplicationIcon(ctx, sideX, iconY, 13*s, theme.accent, typeIdx);
}
function drawBluetooth(ctx,x,y,r,col){
ctx.strokeStyle=toHex(col); ctx.lineWidth=3;
ctx.beginPath();
ctx.moveTo(x,y-r); ctx.lineTo(x,y+r);
ctx.moveTo(x,y-r); ctx.lineTo(x+r*0.7,y-r*0.3);
ctx.lineTo(x,y); ctx.moveTo(x,y); ctx.lineTo(x+r*0.7,y+r*0.3);
ctx.lineTo(x,y+r); ctx.stroke();
}
function drawDateBand(ctx,w,cx,s,theme,gi){
const by=286.5*s, bh=44*s, yc=308.5*s;
ctx.fillStyle=toHex(theme.bandFill); ctx.fillRect(0,by,w,bh);
const txt=toHex(theme.bandText);
drawBluetooth(ctx,36.5*s,yc,10*s,theme.bandText);
const dow=DOW[gi.day_of_week-1], md=MON[gi.month-1]+' '+gi.day;
ctx.fillStyle=txt; ctx.font=`${Math.round(16*s)}px -apple-system,Helvetica,Arial`;
ctx.textBaseline='middle';
ctx.textAlign='center'; ctx.fillText(dow,177.5*s,yc);
ctx.fillText(md,297.5*s,yc);
ctx.textAlign='right'; ctx.fillText(gi.hour>=12?'PM':'AM',465*s,yc);
}
function drawTime(ctx,cx,s,theme,gi){
let hh=gi.hour; if(hh===0)hh=12; else if(hh>12)hh=hh-12;
const hStr=''+hh, mStr=('0'+gi.min).slice(-2), ty=220*s;
ctx.font=`700 ${Math.round(66*s)}px -apple-system,Helvetica,Arial`;
ctx.textBaseline='middle';
ctx.fillStyle=toHex(theme.hours); ctx.textAlign='right'; ctx.fillText(hStr,211*s,ty);
ctx.fillStyle=toHex(theme.minutes); ctx.textAlign='left'; ctx.fillText(mStr,272*s,ty);
ctx.fillStyle=toHex(theme.colon);
ctx.fillRect(238*s,184*s,24*s,20*s);
ctx.fillRect(238*s,235*s,24*s,20*s);
}
function drawBottom(ctx,cx,s,theme,dist,steps){
const valY=366*s, labY=410.5*s, leftX=234*s, rightX=266*s;
ctx.textBaseline='middle';
ctx.fillStyle=toHex(theme.textPrimary); ctx.font=`${Math.round(22*s)}px -apple-system,Helvetica,Arial`;
ctx.textAlign='right'; ctx.fillText(dist,leftX,valY);
ctx.fillStyle=toHex(theme.accent); ctx.font=`${Math.round(16*s)}px -apple-system,Helvetica,Arial`;
ctx.fillText('DST',leftX,labY);
ctx.fillStyle=toHex(theme.textPrimary); ctx.font=`${Math.round(22*s)}px -apple-system,Helvetica,Arial`;
ctx.textAlign='left'; ctx.fillText(steps,rightX,valY);
ctx.fillStyle=toHex(theme.accent);
ctx.fillText('STEP',rightX,labY);
}
// ---- 主渲染 ----
function renderWatch(ctx, W, themeId, data, round){
const cx=W/2, cy=W/2, s=W/500;
ctx.clearRect(0,0,W,W);
ctx.fillStyle='#000'; ctx.fillRect(0,0,W,W);
if(round){ ctx.save(); ctx.beginPath(); ctx.arc(cx,cy,W/2-2,0,7); ctx.clip(); }
const theme=THEMES[themeId];
const p=[data.m[0],data.m[1],data.m[2],data.m[3],data.m[4]];
const idx={TR:0,TL:1,BR:4,BL:3}; // 默认设置
drawRing(ctx,cx,cy,s,theme, p[idx.TR],p[idx.TL],p[idx.BR],p[idx.BL]);
drawBattery(ctx,cx,s,theme,data.battery);
drawTopComplication(ctx,cx,cy,s,theme,1,true, Math.round(data.m[1]*2000).toLocaleString()); // 左上=卡路里
drawTopComplication(ctx,cx,cy,s,theme,0,false, data.temp+'°'); // 右上=天气温度
const gi=data.time;
drawTime(ctx,cx,s,theme,gi);
drawDateBand(ctx,W,cx,s,theme,gi);
drawBottom(ctx,cx,s,theme, (data.m[2]*10).toFixed(1), Math.round(data.m[0]*10000).toLocaleString());
if(round) ctx.restore();
}
// ---- 状态 ----
const state={ themeId:'01', round:true, live:true,
m:[0.84,0.64,0.65,0.80,0.40], battery:78, temp:23 };
function nowGI(){const d=new Date();return {hour:d.getHours(),min:d.getMinutes(),day:d.getDate(),month:d.getMonth()+1,day_of_week:(d.getDay()+1)%7||7};}
const face=document.getElementById('face'), fctx=face.getContext('2d');
function drawMain(){
const gi=state.live?nowGI():{hour:10,min:8,day:9,month:9,day_of_week:3};
renderWatch(fctx,500,state.themeId,{m:state.m,battery:state.battery,temp:state.temp,time:gi},state.round);
}
// 主题按钮
const themesBox=document.getElementById('themes');
ORDER.forEach((id,i)=>{
const b=document.createElement('button'); b.textContent=(i+1)+' '+THEMES[id].name;
b.onclick=()=>{state.themeId=id; [...themesBox.children].forEach(c=>c.classList.remove('active')); b.classList.add('active'); drawMain(); drawGallery();};
themesBox.appendChild(b);
});
themesBox.children[0].classList.add('active');
// 滑块
[0,1,2,3,4].forEach(i=>{
const el=document.getElementById('m'+i), v=document.getElementById('v'+i);
const upd=()=>{state.m[i]=el.value/100; v.textContent=el.value+'%'; drawMain();};
el.oninput=upd; v.textContent=el.value+'%';
});
const bat=document.getElementById('bat'), vbat=document.getElementById('vbat');
bat.oninput=()=>{state.battery=+bat.value; vbat.textContent=bat.value+'%'; drawMain();}; vbat.textContent=bat.value+'%';
const temp=document.getElementById('temp'), vtemp=document.getElementById('vtemp');
temp.oninput=()=>{state.temp=+temp.value; vtemp.textContent=temp.value+'°'; drawMain();}; vtemp.textContent=temp.value+'°';
document.getElementById('round').onchange=e=>{state.round=e.target.checked; drawMain();};
document.getElementById('live').onchange=e=>{state.live=e.target.checked; drawMain();};
// 画廊
const gal=document.getElementById('gallery');
ORDER.forEach(id=>{
const fig=document.createElement('figure');
const c=document.createElement('canvas'); c.width=150; c.height=150;
const cap=document.createElement('figcaption'); cap.textContent=(ORDER.indexOf(id)+1)+' · '+THEMES[id].name;
fig.appendChild(c); fig.appendChild(cap); gal.appendChild(fig);
});
function drawGallery(){
[...gal.children].forEach((fig,i)=>{
const c=fig.querySelector('canvas'), x=c.getContext('2d');
renderWatch(x,150,ORDER[i],{m:state.m,battery:state.battery,temp:state.temp,time:nowGI()},true);
});
}
drawMain(); drawGallery();
setInterval(()=>{ if(state.live) drawMain(); drawGallery(); }, 1000);
</script>
</body>
</html>