faradayfuture commited on
Commit
084c25f
·
verified ·
1 Parent(s): ac42a17

Normalise ep4 to viewer.html (drop view.html); sidebar height now tracks the video column

Browse files
Files changed (8) hide show
  1. ep0/viewer.html +42 -4
  2. ep1/viewer.html +42 -4
  3. ep2/viewer.html +42 -4
  4. ep3/viewer.html +42 -4
  5. ep4/view.html +0 -0
  6. ep4/viewer.html +3 -0
  7. ep5/viewer.html +42 -4
  8. teleop.html +1 -1
ep0/viewer.html CHANGED
@@ -56,6 +56,9 @@ canvas{width:100%;display:block;cursor:pointer}
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
 
 
 
59
  </style></head><body>
60
  <header>
61
  <a class="back" href="../teleop.html">← all episodes</a>
@@ -219,7 +222,7 @@ function handLayout(mirror){
219
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
220
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
221
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
222
- const FING=[['little',64,42,40],['ring',48,76,18],['middle',32,110,6],['index',16,140,20]];
223
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
224
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
225
  for(const [nm,off,cx,top] of FING){
@@ -255,8 +258,16 @@ function handLayout(mirror){
255
  }
256
 
257
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
 
 
 
 
 
 
 
 
258
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
259
- const HW=372, HH=210, HSC=Math.min(HH/300,(HW/2-8)/220);
260
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
261
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
262
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
@@ -267,9 +278,9 @@ function drawTactile(i){
267
  _frame=i; _hit=[];
268
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
269
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
270
- const oy=(HH-300*HSC)/2;
271
  HANDS.forEach((hd,hi)=>{
272
- const ox=14+hi*(HW/2);
273
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
274
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
275
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
@@ -393,4 +404,31 @@ window.addEventListener('load',update);
393
  if(d>0 && master.currentTime>=d-0.08) restart();
394
  },200);
395
  })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  </script></body></html>
 
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
59
+
60
+ .layout{align-items:start}
61
+ .sidecol>.panel:first-child{flex:0 0 auto;min-height:60px;overflow:auto}
62
  </style></head><body>
63
  <header>
64
  <a class="back" href="../teleop.html">← all episodes</a>
 
222
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
223
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
224
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
225
+ const FING=[['little',64,42,70],['ring',48,76,48],['middle',32,110,36],['index',16,140,50]];
226
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
227
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
228
  for(const [nm,off,cx,top] of FING){
 
258
  }
259
 
260
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
261
+ // 内容包围盒: 缩放时按它算, 避免上下留白吃掉尺寸
262
+ const BBOX=(function(){var x0=1e9,y0=1e9,x1=-1e9,y1=-1e9;
263
+ HANDS.forEach(function(h){h.lay.cells.concat(h.lay.outline).forEach(function(o){
264
+ var a=o.rot||0,ca=Math.cos(a),sa=Math.sin(a);
265
+ [[0,0],[o.w,0],[0,o.h],[o.w,o.h]].forEach(function(p){ // 旋转矩形的四个真实角点
266
+ var X=o.x+p[0]*ca-p[1]*sa, Y=o.y+p[0]*sa+p[1]*ca;
267
+ x0=Math.min(x0,X);y0=Math.min(y0,Y);x1=Math.max(x1,X);y1=Math.max(y1,Y);});});});
268
+ return {x:x0-2,y:y0-2,w:x1-x0+4,h:y1-y0+4};})();
269
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
270
+ let HW=372, HH=210, HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
271
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
272
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
273
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
 
278
  _frame=i; _hit=[];
279
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
280
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
281
+ const oy=(HH-BBOX.h*HSC)/2-BBOX.y*HSC;
282
  HANDS.forEach((hd,hi)=>{
283
+ const ox=8+hi*(HW/2)-BBOX.x*HSC;
284
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
285
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
286
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
 
404
  if(d>0 && master.currentTime>=d-0.08) restart();
405
  },200);
406
  })();
407
+
408
+ // ---- 侧栏高度贴合左侧视频列 ----
409
+ function fitHand(){
410
+ var camPanel=document.querySelector('.layout > .panel');
411
+ var spans=document.querySelector('.sidecol > .panel:first-child');
412
+ var tac=document.querySelector('.tacpanel');
413
+ if(!camPanel||!spans||!tac) return;
414
+ spans.style.maxHeight=''; // 先量自然高度
415
+ var availH=camPanel.getBoundingClientRect().height;
416
+ var chrome=tac.getBoundingClientRect().height-cHand.getBoundingClientRect().height;
417
+ var spansH=spans.getBoundingClientRect().height;
418
+ var h=availH-spansH-14-chrome;
419
+ if(h<150){ // 空间不够: 让 spans 滚动, 画布保底 150
420
+ h=150;
421
+ var cap=availH-14-chrome-h;
422
+ if(cap>60) spans.style.maxHeight=Math.floor(cap)+'px';
423
+ }
424
+ h=Math.min(300,Math.max(150,Math.floor(h)));
425
+ var w=Math.max(280,Math.floor(tac.clientWidth-20));
426
+ if(h===HH && w===HW) return;
427
+ HH=h; HW=w; HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
428
+ cHand.style.height=HH+'px';
429
+ drawTactile(_frame);
430
+ }
431
+ window.addEventListener('resize',function(){clearTimeout(window.__fitT);window.__fitT=setTimeout(fitHand,120);});
432
+ window.addEventListener('load',function(){fitHand();setTimeout(fitHand,400);});
433
+ setTimeout(fitHand,150); setTimeout(fitHand,900);
434
  </script></body></html>
ep1/viewer.html CHANGED
@@ -56,6 +56,9 @@ canvas{width:100%;display:block;cursor:pointer}
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
 
 
 
59
  </style></head><body>
60
  <header>
61
  <a class="back" href="../teleop.html">← all episodes</a>
@@ -219,7 +222,7 @@ function handLayout(mirror){
219
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
220
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
221
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
222
- const FING=[['little',64,42,40],['ring',48,76,18],['middle',32,110,6],['index',16,140,20]];
223
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
224
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
225
  for(const [nm,off,cx,top] of FING){
@@ -255,8 +258,16 @@ function handLayout(mirror){
255
  }
256
 
257
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
 
 
 
 
 
 
 
 
258
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
259
- const HW=372, HH=210, HSC=Math.min(HH/300,(HW/2-8)/220);
260
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
261
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
262
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
@@ -267,9 +278,9 @@ function drawTactile(i){
267
  _frame=i; _hit=[];
268
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
269
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
270
- const oy=(HH-300*HSC)/2;
271
  HANDS.forEach((hd,hi)=>{
272
- const ox=14+hi*(HW/2);
273
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
274
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
275
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
@@ -393,4 +404,31 @@ window.addEventListener('load',update);
393
  if(d>0 && master.currentTime>=d-0.08) restart();
394
  },200);
395
  })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  </script></body></html>
 
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
59
+
60
+ .layout{align-items:start}
61
+ .sidecol>.panel:first-child{flex:0 0 auto;min-height:60px;overflow:auto}
62
  </style></head><body>
63
  <header>
64
  <a class="back" href="../teleop.html">← all episodes</a>
 
222
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
223
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
224
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
225
+ const FING=[['little',64,42,70],['ring',48,76,48],['middle',32,110,36],['index',16,140,50]];
226
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
227
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
228
  for(const [nm,off,cx,top] of FING){
 
258
  }
259
 
260
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
261
+ // 内容包围盒: 缩放时按它算, 避免上下留白吃掉尺寸
262
+ const BBOX=(function(){var x0=1e9,y0=1e9,x1=-1e9,y1=-1e9;
263
+ HANDS.forEach(function(h){h.lay.cells.concat(h.lay.outline).forEach(function(o){
264
+ var a=o.rot||0,ca=Math.cos(a),sa=Math.sin(a);
265
+ [[0,0],[o.w,0],[0,o.h],[o.w,o.h]].forEach(function(p){ // 旋转矩形的四个真实角点
266
+ var X=o.x+p[0]*ca-p[1]*sa, Y=o.y+p[0]*sa+p[1]*ca;
267
+ x0=Math.min(x0,X);y0=Math.min(y0,Y);x1=Math.max(x1,X);y1=Math.max(y1,Y);});});});
268
+ return {x:x0-2,y:y0-2,w:x1-x0+4,h:y1-y0+4};})();
269
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
270
+ let HW=372, HH=210, HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
271
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
272
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
273
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
 
278
  _frame=i; _hit=[];
279
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
280
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
281
+ const oy=(HH-BBOX.h*HSC)/2-BBOX.y*HSC;
282
  HANDS.forEach((hd,hi)=>{
283
+ const ox=8+hi*(HW/2)-BBOX.x*HSC;
284
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
285
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
286
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
 
404
  if(d>0 && master.currentTime>=d-0.08) restart();
405
  },200);
406
  })();
407
+
408
+ // ---- 侧栏高度贴合左侧视频列 ----
409
+ function fitHand(){
410
+ var camPanel=document.querySelector('.layout > .panel');
411
+ var spans=document.querySelector('.sidecol > .panel:first-child');
412
+ var tac=document.querySelector('.tacpanel');
413
+ if(!camPanel||!spans||!tac) return;
414
+ spans.style.maxHeight=''; // 先量自然高度
415
+ var availH=camPanel.getBoundingClientRect().height;
416
+ var chrome=tac.getBoundingClientRect().height-cHand.getBoundingClientRect().height;
417
+ var spansH=spans.getBoundingClientRect().height;
418
+ var h=availH-spansH-14-chrome;
419
+ if(h<150){ // 空间不够: 让 spans 滚动, 画布保底 150
420
+ h=150;
421
+ var cap=availH-14-chrome-h;
422
+ if(cap>60) spans.style.maxHeight=Math.floor(cap)+'px';
423
+ }
424
+ h=Math.min(300,Math.max(150,Math.floor(h)));
425
+ var w=Math.max(280,Math.floor(tac.clientWidth-20));
426
+ if(h===HH && w===HW) return;
427
+ HH=h; HW=w; HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
428
+ cHand.style.height=HH+'px';
429
+ drawTactile(_frame);
430
+ }
431
+ window.addEventListener('resize',function(){clearTimeout(window.__fitT);window.__fitT=setTimeout(fitHand,120);});
432
+ window.addEventListener('load',function(){fitHand();setTimeout(fitHand,400);});
433
+ setTimeout(fitHand,150); setTimeout(fitHand,900);
434
  </script></body></html>
ep2/viewer.html CHANGED
@@ -56,6 +56,9 @@ canvas{width:100%;display:block;cursor:pointer}
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
 
 
 
59
  </style></head><body>
60
  <header>
61
  <a class="back" href="../teleop.html">← all episodes</a>
@@ -219,7 +222,7 @@ function handLayout(mirror){
219
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
220
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
221
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
222
- const FING=[['little',64,42,40],['ring',48,76,18],['middle',32,110,6],['index',16,140,20]];
223
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
224
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
225
  for(const [nm,off,cx,top] of FING){
@@ -255,8 +258,16 @@ function handLayout(mirror){
255
  }
256
 
257
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
 
 
 
 
 
 
 
 
258
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
259
- const HW=372, HH=210, HSC=Math.min(HH/300,(HW/2-8)/220);
260
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
261
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
262
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
@@ -267,9 +278,9 @@ function drawTactile(i){
267
  _frame=i; _hit=[];
268
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
269
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
270
- const oy=(HH-300*HSC)/2;
271
  HANDS.forEach((hd,hi)=>{
272
- const ox=14+hi*(HW/2);
273
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
274
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
275
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
@@ -393,4 +404,31 @@ window.addEventListener('load',update);
393
  if(d>0 && master.currentTime>=d-0.08) restart();
394
  },200);
395
  })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  </script></body></html>
 
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
59
+
60
+ .layout{align-items:start}
61
+ .sidecol>.panel:first-child{flex:0 0 auto;min-height:60px;overflow:auto}
62
  </style></head><body>
63
  <header>
64
  <a class="back" href="../teleop.html">← all episodes</a>
 
222
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
223
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
224
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
225
+ const FING=[['little',64,42,70],['ring',48,76,48],['middle',32,110,36],['index',16,140,50]];
226
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
227
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
228
  for(const [nm,off,cx,top] of FING){
 
258
  }
259
 
260
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
261
+ // 内容包围盒: 缩放时按它算, 避免上下留白吃掉尺寸
262
+ const BBOX=(function(){var x0=1e9,y0=1e9,x1=-1e9,y1=-1e9;
263
+ HANDS.forEach(function(h){h.lay.cells.concat(h.lay.outline).forEach(function(o){
264
+ var a=o.rot||0,ca=Math.cos(a),sa=Math.sin(a);
265
+ [[0,0],[o.w,0],[0,o.h],[o.w,o.h]].forEach(function(p){ // 旋转矩形的四个真实角点
266
+ var X=o.x+p[0]*ca-p[1]*sa, Y=o.y+p[0]*sa+p[1]*ca;
267
+ x0=Math.min(x0,X);y0=Math.min(y0,Y);x1=Math.max(x1,X);y1=Math.max(y1,Y);});});});
268
+ return {x:x0-2,y:y0-2,w:x1-x0+4,h:y1-y0+4};})();
269
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
270
+ let HW=372, HH=210, HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
271
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
272
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
273
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
 
278
  _frame=i; _hit=[];
279
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
280
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
281
+ const oy=(HH-BBOX.h*HSC)/2-BBOX.y*HSC;
282
  HANDS.forEach((hd,hi)=>{
283
+ const ox=8+hi*(HW/2)-BBOX.x*HSC;
284
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
285
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
286
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
 
404
  if(d>0 && master.currentTime>=d-0.08) restart();
405
  },200);
406
  })();
407
+
408
+ // ---- 侧栏高度贴合左侧视频列 ----
409
+ function fitHand(){
410
+ var camPanel=document.querySelector('.layout > .panel');
411
+ var spans=document.querySelector('.sidecol > .panel:first-child');
412
+ var tac=document.querySelector('.tacpanel');
413
+ if(!camPanel||!spans||!tac) return;
414
+ spans.style.maxHeight=''; // 先量自然高度
415
+ var availH=camPanel.getBoundingClientRect().height;
416
+ var chrome=tac.getBoundingClientRect().height-cHand.getBoundingClientRect().height;
417
+ var spansH=spans.getBoundingClientRect().height;
418
+ var h=availH-spansH-14-chrome;
419
+ if(h<150){ // 空间不够: 让 spans 滚动, 画布保底 150
420
+ h=150;
421
+ var cap=availH-14-chrome-h;
422
+ if(cap>60) spans.style.maxHeight=Math.floor(cap)+'px';
423
+ }
424
+ h=Math.min(300,Math.max(150,Math.floor(h)));
425
+ var w=Math.max(280,Math.floor(tac.clientWidth-20));
426
+ if(h===HH && w===HW) return;
427
+ HH=h; HW=w; HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
428
+ cHand.style.height=HH+'px';
429
+ drawTactile(_frame);
430
+ }
431
+ window.addEventListener('resize',function(){clearTimeout(window.__fitT);window.__fitT=setTimeout(fitHand,120);});
432
+ window.addEventListener('load',function(){fitHand();setTimeout(fitHand,400);});
433
+ setTimeout(fitHand,150); setTimeout(fitHand,900);
434
  </script></body></html>
ep3/viewer.html CHANGED
@@ -56,6 +56,9 @@ canvas{width:100%;display:block;cursor:pointer}
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
 
 
 
59
  </style></head><body>
60
  <header>
61
  <a class="back" href="../teleop.html">← all episodes</a>
@@ -219,7 +222,7 @@ function handLayout(mirror){
219
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
220
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
221
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
222
- const FING=[['little',64,42,40],['ring',48,76,18],['middle',32,110,6],['index',16,140,20]];
223
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
224
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
225
  for(const [nm,off,cx,top] of FING){
@@ -255,8 +258,16 @@ function handLayout(mirror){
255
  }
256
 
257
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
 
 
 
 
 
 
 
 
258
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
259
- const HW=372, HH=210, HSC=Math.min(HH/300,(HW/2-8)/220);
260
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
261
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
262
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
@@ -267,9 +278,9 @@ function drawTactile(i){
267
  _frame=i; _hit=[];
268
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
269
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
270
- const oy=(HH-300*HSC)/2;
271
  HANDS.forEach((hd,hi)=>{
272
- const ox=14+hi*(HW/2);
273
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
274
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
275
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
@@ -393,4 +404,31 @@ window.addEventListener('load',update);
393
  if(d>0 && master.currentTime>=d-0.08) restart();
394
  },200);
395
  })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  </script></body></html>
 
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
59
+
60
+ .layout{align-items:start}
61
+ .sidecol>.panel:first-child{flex:0 0 auto;min-height:60px;overflow:auto}
62
  </style></head><body>
63
  <header>
64
  <a class="back" href="../teleop.html">← all episodes</a>
 
222
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
223
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
224
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
225
+ const FING=[['little',64,42,70],['ring',48,76,48],['middle',32,110,36],['index',16,140,50]];
226
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
227
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
228
  for(const [nm,off,cx,top] of FING){
 
258
  }
259
 
260
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
261
+ // 内容包围盒: 缩放时按它算, 避免上下留白吃掉尺寸
262
+ const BBOX=(function(){var x0=1e9,y0=1e9,x1=-1e9,y1=-1e9;
263
+ HANDS.forEach(function(h){h.lay.cells.concat(h.lay.outline).forEach(function(o){
264
+ var a=o.rot||0,ca=Math.cos(a),sa=Math.sin(a);
265
+ [[0,0],[o.w,0],[0,o.h],[o.w,o.h]].forEach(function(p){ // 旋转矩形的四个真实角点
266
+ var X=o.x+p[0]*ca-p[1]*sa, Y=o.y+p[0]*sa+p[1]*ca;
267
+ x0=Math.min(x0,X);y0=Math.min(y0,Y);x1=Math.max(x1,X);y1=Math.max(y1,Y);});});});
268
+ return {x:x0-2,y:y0-2,w:x1-x0+4,h:y1-y0+4};})();
269
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
270
+ let HW=372, HH=210, HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
271
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
272
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
273
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
 
278
  _frame=i; _hit=[];
279
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
280
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
281
+ const oy=(HH-BBOX.h*HSC)/2-BBOX.y*HSC;
282
  HANDS.forEach((hd,hi)=>{
283
+ const ox=8+hi*(HW/2)-BBOX.x*HSC;
284
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
285
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
286
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
 
404
  if(d>0 && master.currentTime>=d-0.08) restart();
405
  },200);
406
  })();
407
+
408
+ // ---- 侧栏高度贴合左侧视频列 ----
409
+ function fitHand(){
410
+ var camPanel=document.querySelector('.layout > .panel');
411
+ var spans=document.querySelector('.sidecol > .panel:first-child');
412
+ var tac=document.querySelector('.tacpanel');
413
+ if(!camPanel||!spans||!tac) return;
414
+ spans.style.maxHeight=''; // 先量自然高度
415
+ var availH=camPanel.getBoundingClientRect().height;
416
+ var chrome=tac.getBoundingClientRect().height-cHand.getBoundingClientRect().height;
417
+ var spansH=spans.getBoundingClientRect().height;
418
+ var h=availH-spansH-14-chrome;
419
+ if(h<150){ // 空间不够: 让 spans 滚动, 画布保底 150
420
+ h=150;
421
+ var cap=availH-14-chrome-h;
422
+ if(cap>60) spans.style.maxHeight=Math.floor(cap)+'px';
423
+ }
424
+ h=Math.min(300,Math.max(150,Math.floor(h)));
425
+ var w=Math.max(280,Math.floor(tac.clientWidth-20));
426
+ if(h===HH && w===HW) return;
427
+ HH=h; HW=w; HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
428
+ cHand.style.height=HH+'px';
429
+ drawTactile(_frame);
430
+ }
431
+ window.addEventListener('resize',function(){clearTimeout(window.__fitT);window.__fitT=setTimeout(fitHand,120);});
432
+ window.addEventListener('load',function(){fitHand();setTimeout(fitHand,400);});
433
+ setTimeout(fitHand,150); setTimeout(fitHand,900);
434
  </script></body></html>
ep4/view.html DELETED
The diff for this file is too large to render. See raw diff
 
ep4/viewer.html ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6bf2fe4455605ca1af3102dd615e0d0fd8206bc8b62fe3af920f46df4ad3953d
3
+ size 607139
ep5/viewer.html CHANGED
@@ -56,6 +56,9 @@ canvas{width:100%;display:block;cursor:pointer}
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
 
 
 
59
  </style></head><body>
60
  <header>
61
  <a class="back" href="../teleop.html">← all episodes</a>
@@ -219,7 +222,7 @@ function handLayout(mirror){
219
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
220
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
221
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
222
- const FING=[['little',64,42,40],['ring',48,76,18],['middle',32,110,6],['index',16,140,20]];
223
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
224
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
225
  for(const [nm,off,cx,top] of FING){
@@ -255,8 +258,16 @@ function handLayout(mirror){
255
  }
256
 
257
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
 
 
 
 
 
 
 
 
258
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
259
- const HW=372, HH=210, HSC=Math.min(HH/300,(HW/2-8)/220);
260
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
261
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
262
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
@@ -267,9 +278,9 @@ function drawTactile(i){
267
  _frame=i; _hit=[];
268
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
269
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
270
- const oy=(HH-300*HSC)/2;
271
  HANDS.forEach((hd,hi)=>{
272
- const ox=14+hi*(HW/2);
273
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
274
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
275
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
@@ -393,4 +404,31 @@ window.addEventListener('load',update);
393
  if(d>0 && master.currentTime>=d-0.08) restart();
394
  },200);
395
  })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  </script></body></html>
 
56
  .tac-legend .grad{display:inline-block;width:70px;height:8px;border-radius:2px}
57
  .tac-legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;background:#1b2028;border:1px solid #2a3340}
58
  @media(max-width:1200px){.layout{grid-template-columns:1fr}}
59
+
60
+ .layout{align-items:start}
61
+ .sidecol>.panel:first-child{flex:0 0 auto;min-height:60px;overflow:auto}
62
  </style></head><body>
63
  <header>
64
  <a class="back" href="../teleop.html">← all episodes</a>
 
222
  const U=8.8, PX=14, PY=150, GAP=1.5; // 手掌网格单位
223
  const CW=9.5,CH=5.4,G=1.3, PH=8*(CH+G)-G; // 指尖阵列 20.3 x 52.3
224
  // 从左到右: 小指, 无名指, 中指, 食指(食指紧邻右侧拇指)
225
+ const FING=[['little',64,42,70],['ring',48,76,48],['middle',32,110,36],['index',16,140,50]];
226
  outline.push({x:10,y:146,w:140,h:131,r:19,palm:true}); // 手掌轮廓(完整掌面)
227
  outline.push({x:40,y:275,w:56,h:19,r:9}); // 手腕
228
  for(const [nm,off,cx,top] of FING){
 
258
  }
259
 
260
  const HANDS=[{name:'left',base:0,lay:handLayout(true)},{name:'right',base:TAC_N,lay:handLayout(false)}];
261
+ // 内容包围盒: 缩放时按它算, 避免上下留白吃掉尺寸
262
+ const BBOX=(function(){var x0=1e9,y0=1e9,x1=-1e9,y1=-1e9;
263
+ HANDS.forEach(function(h){h.lay.cells.concat(h.lay.outline).forEach(function(o){
264
+ var a=o.rot||0,ca=Math.cos(a),sa=Math.sin(a);
265
+ [[0,0],[o.w,0],[0,o.h],[o.w,o.h]].forEach(function(p){ // 旋转矩形的四个真实角点
266
+ var X=o.x+p[0]*ca-p[1]*sa, Y=o.y+p[0]*sa+p[1]*ca;
267
+ x0=Math.min(x0,X);y0=Math.min(y0,Y);x1=Math.max(x1,X);y1=Math.max(y1,Y);});});});
268
+ return {x:x0-2,y:y0-2,w:x1-x0+4,h:y1-y0+4};})();
269
  const cHand=document.getElementById('cHand'), tacTip=document.getElementById('tacTip');
270
+ let HW=372, HH=210, HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
271
  cHand.style.width=HW+'px'; cHand.style.height=HH+'px';
272
  document.getElementById('tacGrad').style.background='linear-gradient(90deg,'+RAMP.map(c=>`rgb(${c[0]},${c[1]},${c[2]})`).join(',')+')';
273
  document.getElementById('tacPeakLbl').textContent=TAC_PEAK;
 
278
  _frame=i; _hit=[];
279
  const dpr=devicePixelRatio||1, ctx=cHand.getContext('2d');
280
  cHand.width=HW*dpr; cHand.height=HH*dpr; ctx.setTransform(dpr,0,0,dpr,0,0); ctx.clearRect(0,0,HW,HH);
281
+ const oy=(HH-BBOX.h*HSC)/2-BBOX.y*HSC;
282
  HANDS.forEach((hd,hi)=>{
283
+ const ox=8+hi*(HW/2)-BBOX.x*HSC;
284
  ctx.save(); ctx.translate(ox,oy); ctx.scale(HSC,HSC);
285
  ctx.strokeStyle='#26313f'; ctx.lineWidth=1.1;
286
  for(const o of hd.lay.outline){ctx.save(); ctx.strokeStyle=o.palm?'#33475c':'#26313f';
 
404
  if(d>0 && master.currentTime>=d-0.08) restart();
405
  },200);
406
  })();
407
+
408
+ // ---- 侧栏高度贴合左侧视频列 ----
409
+ function fitHand(){
410
+ var camPanel=document.querySelector('.layout > .panel');
411
+ var spans=document.querySelector('.sidecol > .panel:first-child');
412
+ var tac=document.querySelector('.tacpanel');
413
+ if(!camPanel||!spans||!tac) return;
414
+ spans.style.maxHeight=''; // 先量自然高度
415
+ var availH=camPanel.getBoundingClientRect().height;
416
+ var chrome=tac.getBoundingClientRect().height-cHand.getBoundingClientRect().height;
417
+ var spansH=spans.getBoundingClientRect().height;
418
+ var h=availH-spansH-14-chrome;
419
+ if(h<150){ // 空间不够: 让 spans 滚动, 画布保底 150
420
+ h=150;
421
+ var cap=availH-14-chrome-h;
422
+ if(cap>60) spans.style.maxHeight=Math.floor(cap)+'px';
423
+ }
424
+ h=Math.min(300,Math.max(150,Math.floor(h)));
425
+ var w=Math.max(280,Math.floor(tac.clientWidth-20));
426
+ if(h===HH && w===HW) return;
427
+ HH=h; HW=w; HSC=Math.min(HH/BBOX.h,(HW/2-8)/BBOX.w);
428
+ cHand.style.height=HH+'px';
429
+ drawTactile(_frame);
430
+ }
431
+ window.addEventListener('resize',function(){clearTimeout(window.__fitT);window.__fitT=setTimeout(fitHand,120);});
432
+ window.addEventListener('load',function(){fitHand();setTimeout(fitHand,400);});
433
+ setTimeout(fitHand,150); setTimeout(fitHand,900);
434
  </script></body></html>
teleop.html CHANGED
@@ -27,6 +27,6 @@ header{padding:22px 26px 6px}h1{margin:0 0 4px;font-size:22px}
27
  <a class="card" href="ep1/viewer.html"><div class="ph"><img src="ep1/thumb.jpg" alt="episode 1"><span class="pi"></span></div><div class="m"><div class="t">Episode 1 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>18.8s</span><span>563 frames</span></div></div></a>
28
  <a class="card" href="ep2/viewer.html"><div class="ph"><img src="ep2/thumb.jpg" alt="episode 2"><span class="pi"></span></div><div class="m"><div class="t">Episode 2 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>18.7s</span><span>562 frames</span></div></div></a>
29
  <a class="card" href="ep3/viewer.html"><div class="ph"><img src="ep3/thumb.jpg" alt="episode 3"><span class="pi"></span></div><div class="m"><div class="t">Episode 3 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>20.5s</span><span>614 frames</span></div></div></a>
30
- <a class="card" href="ep4/view.html"><div class="ph"><img src="ep4/thumb.jpg" alt="episode 4"><span class="pi"></span></div><div class="m"><div class="t">Episode 4 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>22.4s</span><span>673 frames</span></div></div></a>
31
  <a class="card" href="ep5/viewer.html"><div class="ph"><img src="ep5/thumb.jpg" alt="episode 5"><span class="pi"></span></div><div class="m"><div class="t">Episode 5 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>18.4s</span><span>553 frames</span></div></div></a></div>
32
  </body></html>
 
27
  <a class="card" href="ep1/viewer.html"><div class="ph"><img src="ep1/thumb.jpg" alt="episode 1"><span class="pi"></span></div><div class="m"><div class="t">Episode 1 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>18.8s</span><span>563 frames</span></div></div></a>
28
  <a class="card" href="ep2/viewer.html"><div class="ph"><img src="ep2/thumb.jpg" alt="episode 2"><span class="pi"></span></div><div class="m"><div class="t">Episode 2 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>18.7s</span><span>562 frames</span></div></div></a>
29
  <a class="card" href="ep3/viewer.html"><div class="ph"><img src="ep3/thumb.jpg" alt="episode 3"><span class="pi"></span></div><div class="m"><div class="t">Episode 3 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>20.5s</span><span>614 frames</span></div></div></a>
30
+ <a class="card" href="ep4/viewer.html"><div class="ph"><img src="ep4/thumb.jpg" alt="episode 4"><span class="pi"></span></div><div class="m"><div class="t">Episode 4 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>22.4s</span><span>673 frames</span></div></div></a>
31
  <a class="card" href="ep5/viewer.html"><div class="ph"><img src="ep5/thumb.jpg" alt="episode 5"><span class="pi"></span></div><div class="m"><div class="t">Episode 5 · Pick up the water bottle and put it in the basket</div><div class="r"><span class="chip">A2</span><span>18.4s</span><span>553 frames</span></div></div></a></div>
32
  </body></html>