faradayfuture commited on
Commit
164dee5
·
verified ·
1 Parent(s): 8103ab6

egoSample viewers: fix autoplay in WeChat/iOS in-app WebViews (no canplay wait, retry on WeixinJSBridgeReady/first tap)

Browse files
egoSample/ego-legacy/demo02/viewer.html CHANGED
@@ -286,22 +286,35 @@ window.addEventListener('load',update);update();
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
- function autoStart(){
 
290
  if(started||playing)return;
291
  var vs=vlist();
292
- if(!vs.length||!vs.every(function(v){return v.readyState>=2;}))return;
293
  started=true; if(iv){clearInterval(iv);iv=null;}
294
  vs.forEach(function(v){v.muted=true;});
295
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
296
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
297
  }).catch(function(){
298
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
 
299
  });
300
  }
301
  vlist().forEach(function(v){
302
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
303
  });
304
- window.addEventListener('load',autoStart);
 
 
 
 
 
 
 
 
 
 
 
305
  iv=setInterval(autoStart,400);
306
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
307
  })();
 
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
+ var t0=Date.now(), armed=false;
290
+ function autoStart(force){
291
  if(started||playing)return;
292
  var vs=vlist();
293
+ if(!vs.length||(!vs.every(function(v){return v.readyState>=2;})&&!(force||Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}))))return;
294
  started=true; if(iv){clearInterval(iv);iv=null;}
295
  vs.forEach(function(v){v.muted=true;});
296
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
297
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
298
  }).catch(function(){
299
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
300
+ started=false; arm();
301
  });
302
  }
303
  vlist().forEach(function(v){
304
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
305
  });
306
+ /* in-app WebViews (WeChat) ignore preload and may refuse autoplay: don't wait for
307
+ readyState>=2 forever, and retry on WeixinJSBridgeReady / the first tap */
308
+ function arm(){
309
+ if(armed)return; armed=true;
310
+ function once(ev){ if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
311
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
312
+ armed=false; autoStart(true); }
313
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
314
+ if(window.WeixinJSBridge&&WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},function(){autoStart(true);});
315
+ }
316
+ document.addEventListener('WeixinJSBridgeReady',function(){autoStart(true);},false);
317
+ window.addEventListener('load',function(){autoStart();});
318
  iv=setInterval(autoStart,400);
319
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
320
  })();
egoSample/ego-legacy/demo05/viewer.html CHANGED
@@ -286,22 +286,35 @@ window.addEventListener('load',update);update();
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
- function autoStart(){
 
290
  if(started||playing)return;
291
  var vs=vlist();
292
- if(!vs.length||!vs.every(function(v){return v.readyState>=2;}))return;
293
  started=true; if(iv){clearInterval(iv);iv=null;}
294
  vs.forEach(function(v){v.muted=true;});
295
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
296
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
297
  }).catch(function(){
298
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
 
299
  });
300
  }
301
  vlist().forEach(function(v){
302
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
303
  });
304
- window.addEventListener('load',autoStart);
 
 
 
 
 
 
 
 
 
 
 
305
  iv=setInterval(autoStart,400);
306
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
307
  })();
 
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
+ var t0=Date.now(), armed=false;
290
+ function autoStart(force){
291
  if(started||playing)return;
292
  var vs=vlist();
293
+ if(!vs.length||(!vs.every(function(v){return v.readyState>=2;})&&!(force||Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}))))return;
294
  started=true; if(iv){clearInterval(iv);iv=null;}
295
  vs.forEach(function(v){v.muted=true;});
296
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
297
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
298
  }).catch(function(){
299
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
300
+ started=false; arm();
301
  });
302
  }
303
  vlist().forEach(function(v){
304
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
305
  });
306
+ /* in-app WebViews (WeChat) ignore preload and may refuse autoplay: don't wait for
307
+ readyState>=2 forever, and retry on WeixinJSBridgeReady / the first tap */
308
+ function arm(){
309
+ if(armed)return; armed=true;
310
+ function once(ev){ if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
311
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
312
+ armed=false; autoStart(true); }
313
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
314
+ if(window.WeixinJSBridge&&WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},function(){autoStart(true);});
315
+ }
316
+ document.addEventListener('WeixinJSBridgeReady',function(){autoStart(true);},false);
317
+ window.addEventListener('load',function(){autoStart();});
318
  iv=setInterval(autoStart,400);
319
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
320
  })();
egoSample/ego-legacy/demo07/viewer.html CHANGED
@@ -286,22 +286,35 @@ window.addEventListener('load',update);update();
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
- function autoStart(){
 
290
  if(started||playing)return;
291
  var vs=vlist();
292
- if(!vs.length||!vs.every(function(v){return v.readyState>=2;}))return;
293
  started=true; if(iv){clearInterval(iv);iv=null;}
294
  vs.forEach(function(v){v.muted=true;});
295
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
296
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
297
  }).catch(function(){
298
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
 
299
  });
300
  }
301
  vlist().forEach(function(v){
302
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
303
  });
304
- window.addEventListener('load',autoStart);
 
 
 
 
 
 
 
 
 
 
 
305
  iv=setInterval(autoStart,400);
306
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
307
  })();
 
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
+ var t0=Date.now(), armed=false;
290
+ function autoStart(force){
291
  if(started||playing)return;
292
  var vs=vlist();
293
+ if(!vs.length||(!vs.every(function(v){return v.readyState>=2;})&&!(force||Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}))))return;
294
  started=true; if(iv){clearInterval(iv);iv=null;}
295
  vs.forEach(function(v){v.muted=true;});
296
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
297
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
298
  }).catch(function(){
299
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
300
+ started=false; arm();
301
  });
302
  }
303
  vlist().forEach(function(v){
304
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
305
  });
306
+ /* in-app WebViews (WeChat) ignore preload and may refuse autoplay: don't wait for
307
+ readyState>=2 forever, and retry on WeixinJSBridgeReady / the first tap */
308
+ function arm(){
309
+ if(armed)return; armed=true;
310
+ function once(ev){ if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
311
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
312
+ armed=false; autoStart(true); }
313
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
314
+ if(window.WeixinJSBridge&&WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},function(){autoStart(true);});
315
+ }
316
+ document.addEventListener('WeixinJSBridgeReady',function(){autoStart(true);},false);
317
+ window.addEventListener('load',function(){autoStart();});
318
  iv=setInterval(autoStart,400);
319
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
320
  })();
egoSample/ego-legacy/demo08/viewer.html CHANGED
@@ -286,22 +286,35 @@ window.addEventListener('load',update);update();
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
- function autoStart(){
 
290
  if(started||playing)return;
291
  var vs=vlist();
292
- if(!vs.length||!vs.every(function(v){return v.readyState>=2;}))return;
293
  started=true; if(iv){clearInterval(iv);iv=null;}
294
  vs.forEach(function(v){v.muted=true;});
295
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
296
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
297
  }).catch(function(){
298
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
 
299
  });
300
  }
301
  vlist().forEach(function(v){
302
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
303
  });
304
- window.addEventListener('load',autoStart);
 
 
 
 
 
 
 
 
 
 
 
305
  iv=setInterval(autoStart,400);
306
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
307
  })();
 
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
+ var t0=Date.now(), armed=false;
290
+ function autoStart(force){
291
  if(started||playing)return;
292
  var vs=vlist();
293
+ if(!vs.length||(!vs.every(function(v){return v.readyState>=2;})&&!(force||Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}))))return;
294
  started=true; if(iv){clearInterval(iv);iv=null;}
295
  vs.forEach(function(v){v.muted=true;});
296
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
297
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
298
  }).catch(function(){
299
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
300
+ started=false; arm();
301
  });
302
  }
303
  vlist().forEach(function(v){
304
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
305
  });
306
+ /* in-app WebViews (WeChat) ignore preload and may refuse autoplay: don't wait for
307
+ readyState>=2 forever, and retry on WeixinJSBridgeReady / the first tap */
308
+ function arm(){
309
+ if(armed)return; armed=true;
310
+ function once(ev){ if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
311
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
312
+ armed=false; autoStart(true); }
313
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
314
+ if(window.WeixinJSBridge&&WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},function(){autoStart(true);});
315
+ }
316
+ document.addEventListener('WeixinJSBridgeReady',function(){autoStart(true);},false);
317
+ window.addEventListener('load',function(){autoStart();});
318
  iv=setInterval(autoStart,400);
319
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
320
  })();
egoSample/ego-legacy/demo09/viewer.html CHANGED
@@ -286,22 +286,35 @@ window.addEventListener('load',update);update();
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
- function autoStart(){
 
290
  if(started||playing)return;
291
  var vs=vlist();
292
- if(!vs.length||!vs.every(function(v){return v.readyState>=2;}))return;
293
  started=true; if(iv){clearInterval(iv);iv=null;}
294
  vs.forEach(function(v){v.muted=true;});
295
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
296
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
297
  }).catch(function(){
298
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
 
299
  });
300
  }
301
  vlist().forEach(function(v){
302
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
303
  });
304
- window.addEventListener('load',autoStart);
 
 
 
 
 
 
 
 
 
 
 
305
  iv=setInterval(autoStart,400);
306
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
307
  })();
 
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
+ var t0=Date.now(), armed=false;
290
+ function autoStart(force){
291
  if(started||playing)return;
292
  var vs=vlist();
293
+ if(!vs.length||(!vs.every(function(v){return v.readyState>=2;})&&!(force||Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}))))return;
294
  started=true; if(iv){clearInterval(iv);iv=null;}
295
  vs.forEach(function(v){v.muted=true;});
296
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
297
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
298
  }).catch(function(){
299
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
300
+ started=false; arm();
301
  });
302
  }
303
  vlist().forEach(function(v){
304
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
305
  });
306
+ /* in-app WebViews (WeChat) ignore preload and may refuse autoplay: don't wait for
307
+ readyState>=2 forever, and retry on WeixinJSBridgeReady / the first tap */
308
+ function arm(){
309
+ if(armed)return; armed=true;
310
+ function once(ev){ if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
311
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
312
+ armed=false; autoStart(true); }
313
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
314
+ if(window.WeixinJSBridge&&WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},function(){autoStart(true);});
315
+ }
316
+ document.addEventListener('WeixinJSBridgeReady',function(){autoStart(true);},false);
317
+ window.addEventListener('load',function(){autoStart();});
318
  iv=setInterval(autoStart,400);
319
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
320
  })();
egoSample/ego-legacy/demo10/viewer.html CHANGED
@@ -286,22 +286,35 @@ window.addEventListener('load',update);update();
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
- function autoStart(){
 
290
  if(started||playing)return;
291
  var vs=vlist();
292
- if(!vs.length||!vs.every(function(v){return v.readyState>=2;}))return;
293
  started=true; if(iv){clearInterval(iv);iv=null;}
294
  vs.forEach(function(v){v.muted=true;});
295
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
296
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
297
  }).catch(function(){
298
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
 
299
  });
300
  }
301
  vlist().forEach(function(v){
302
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
303
  });
304
- window.addEventListener('load',autoStart);
 
 
 
 
 
 
 
 
 
 
 
305
  iv=setInterval(autoStart,400);
306
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
307
  })();
 
286
  (function(){
287
  var started=false, iv=null;
288
  function vlist(){try{return Object.values(videos);}catch(e){return [];}}
289
+ var t0=Date.now(), armed=false;
290
+ function autoStart(force){
291
  if(started||playing)return;
292
  var vs=vlist();
293
+ if(!vs.length||(!vs.every(function(v){return v.readyState>=2;})&&!(force||Date.now()-t0>1500&&vs.every(function(v){return v.readyState>=1||v.src;}))))return;
294
  started=true; if(iv){clearInterval(iv);iv=null;}
295
  vs.forEach(function(v){v.muted=true;});
296
  Promise.all(vs.map(function(v){return Promise.resolve(v.play());})).then(function(){
297
  playing=true; playBtn.innerHTML='❙❙'; requestAnimationFrame(loop);
298
  }).catch(function(){
299
  vs.forEach(function(v){try{v.pause();}catch(e){}}); playing=false; playBtn.innerHTML='▶';
300
+ started=false; arm();
301
  });
302
  }
303
  vlist().forEach(function(v){
304
  v.addEventListener('canplay',autoStart); v.addEventListener('loadeddata',autoStart);
305
  });
306
+ /* in-app WebViews (WeChat) ignore preload and may refuse autoplay: don't wait for
307
+ readyState>=2 forever, and retry on WeixinJSBridgeReady / the first tap */
308
+ function arm(){
309
+ if(armed)return; armed=true;
310
+ function once(ev){ if(ev&&ev.target&&ev.target.closest&&ev.target.closest('#play'))return;
311
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
312
+ armed=false; autoStart(true); }
313
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
314
+ if(window.WeixinJSBridge&&WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},function(){autoStart(true);});
315
+ }
316
+ document.addEventListener('WeixinJSBridgeReady',function(){autoStart(true);},false);
317
+ window.addEventListener('load',function(){autoStart();});
318
  iv=setInterval(autoStart,400);
319
  setTimeout(function(){if(iv){clearInterval(iv);iv=null;}},90000);
320
  })();
egoSample/faberu.html CHANGED
@@ -90,6 +90,31 @@ const esc=s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&g
90
  const fmt=t=>{t=Math.max(0,t);const m=Math.floor(t/60),s=t%60;return m+':'+s.toFixed(2).padStart(5,'0');};
91
  const skc=s=>SKC[s]||'#666';
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  function select(i){ EP=EPS[i]; SIG=null; cancelAnimationFrame(raf); clearInterval(syncT); playing=false; render(); loadSig(EP.slug); }
94
  function loadSig(slug){ window.EPDATA=window.EPDATA||{};
95
  // start every download at once; core first-priority, map next, scans next, depth-hover grid last
@@ -101,7 +126,7 @@ function loadSig(slug){ window.EPDATA=window.EPDATA||{};
101
  scansP.then(merge('scans',F=>{ let c=0; SIG.clouds={}; Object.entries(F.hdr.clouds).forEach(([k,fr])=>{ SIG.clouds[k]=fr.map(f=>Object.assign({t:f.t},decPts(F,c++,f.n))); }); }))
102
  .then(()=>fetchBin(P('dgrid'),null,'low')).then(b=>{ if(!b||!SIG) return; const F=parseFUP(b), n=F.hdr.w*F.hdr.h; SIG.depthGrid=F.hdr.offs.map((o,k)=>new Uint16Array(F.buf.buffer,F.base+o,n)); });
103
  updateUI(master.currentTime||0);
104
- const go=()=>{ if(_toggle&&!playing) _toggle(); }; if(master.readyState>=3) go(); else master.addEventListener('canplay',go,{once:true}); };
105
  if(window.EPDATA[slug]){ done(); return; }
106
  fetchBin(P('core'),null).then(b=>{ window.EPDATA[slug]=JSON.parse(new TextDecoder().decode(b)); done(); })
107
  .catch(()=>{ const s=document.createElement('script'); s.src='epdata/'+slug+'.js'; s.onload=done; document.body.appendChild(s); }); }
 
90
  const fmt=t=>{t=Math.max(0,t);const m=Math.floor(t/60),s=t%60;return m+':'+s.toFixed(2).padStart(5,'0');};
91
  const skc=s=>SKC[s]||'#666';
92
 
93
+ /* autoplay for in-app WebViews (WeChat iOS/Android X5): they ignore preload, so
94
+ canplay never fires until play() is called — don't wait on it forever. If play()
95
+ is refused, roll back to paused and retry on WeixinJSBridgeReady / first tap. */
96
+ let _autoArmed=false;
97
+ function _autoStart(){
98
+ if(!master||!_toggle) return;
99
+ if(!playing) _toggle();
100
+ const m=master; Promise.resolve(m.play()).catch(()=>{ if(playing && master===m) _toggle(); _armRetry(); });
101
+ }
102
+ function _armRetry(){
103
+ if(_autoArmed) return; _autoArmed=true;
104
+ const once=ev=>{ if(ev && ev.target && ev.target.closest && ev.target.closest('#play')) return;
105
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
106
+ _autoArmed=false; if(!playing) _autoStart(); };
107
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
108
+ const wx=()=>{ if(!playing) _autoStart(); };
109
+ if(window.WeixinJSBridge && WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},wx);
110
+ }
111
+ document.addEventListener('WeixinJSBridgeReady',()=>{ if(master && !playing) _autoStart(); },false);
112
+ function _whenReady(go){
113
+ if(master.readyState>=3){ go(); return; }
114
+ let fired=false; const f=()=>{ if(fired) return; fired=true; go(); };
115
+ master.addEventListener('canplay',f,{once:true}); setTimeout(f,1200);
116
+ }
117
+
118
  function select(i){ EP=EPS[i]; SIG=null; cancelAnimationFrame(raf); clearInterval(syncT); playing=false; render(); loadSig(EP.slug); }
119
  function loadSig(slug){ window.EPDATA=window.EPDATA||{};
120
  // start every download at once; core first-priority, map next, scans next, depth-hover grid last
 
126
  scansP.then(merge('scans',F=>{ let c=0; SIG.clouds={}; Object.entries(F.hdr.clouds).forEach(([k,fr])=>{ SIG.clouds[k]=fr.map(f=>Object.assign({t:f.t},decPts(F,c++,f.n))); }); }))
127
  .then(()=>fetchBin(P('dgrid'),null,'low')).then(b=>{ if(!b||!SIG) return; const F=parseFUP(b), n=F.hdr.w*F.hdr.h; SIG.depthGrid=F.hdr.offs.map((o,k)=>new Uint16Array(F.buf.buffer,F.base+o,n)); });
128
  updateUI(master.currentTime||0);
129
+ if(!playing) _whenReady(_autoStart); };
130
  if(window.EPDATA[slug]){ done(); return; }
131
  fetchBin(P('core'),null).then(b=>{ window.EPDATA[slug]=JSON.parse(new TextDecoder().decode(b)); done(); })
132
  .catch(()=>{ const s=document.createElement('script'); s.src='epdata/'+slug+'.js'; s.onload=done; document.body.appendChild(s); }); }
egoSample/jizhi.html CHANGED
@@ -98,6 +98,31 @@ const AXR = ['#ff0000','#00ff00','#0000ff'];
98
  let cur=-1, vids=[], master=null, playing=false, EP=null, SIG=null, raf=0, syncTimer=0, _toggle=null, _lastCi=-2;
99
  let orbO={yaw:0.6,pitch:-0.45,drag:0};
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  const esc = s => (s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
102
  const fmt = t => { t=Math.max(0,t); const m=Math.floor(t/60), s=t%60; return m+":"+s.toFixed(2).padStart(5,'0'); };
103
  const skc = s => SKC[s]||'#666';
@@ -115,8 +140,7 @@ function loadSig(slug){
115
  window.EPDATA=window.EPDATA||{};
116
  const done=()=>{ SIG=window.EPDATA[slug]; if(!SIG) return; SIG._fw=null; sizeCanvases(); drawCharts();
117
  updateUI(master?master.currentTime:0);
118
- const go=()=>{ if(_toggle && !playing) _toggle(); };
119
- if(!master){} else if(master.readyState>=3) go(); else master.addEventListener('canplay',go,{once:true}); };
120
  if(window.EPDATA[slug]){ done(); return; }
121
  const s=document.createElement('script'); s.id='sigjs'; s.src='epdata/'+slug+'.js';
122
  s.onload=done; s.onerror=()=>{ SIG=null; }; document.body.appendChild(s);
 
98
  let cur=-1, vids=[], master=null, playing=false, EP=null, SIG=null, raf=0, syncTimer=0, _toggle=null, _lastCi=-2;
99
  let orbO={yaw:0.6,pitch:-0.45,drag:0};
100
 
101
+ /* autoplay for in-app WebViews (WeChat iOS/Android X5): they ignore preload, so
102
+ canplay never fires until play() is called — don't wait on it forever. If play()
103
+ is refused, roll back to paused and retry on WeixinJSBridgeReady / first tap. */
104
+ let _autoArmed=false;
105
+ function _autoStart(){
106
+ if(!master||!_toggle) return;
107
+ if(!playing) _toggle();
108
+ const m=master; Promise.resolve(m.play()).catch(()=>{ if(playing && master===m) _toggle(); _armRetry(); });
109
+ }
110
+ function _armRetry(){
111
+ if(_autoArmed) return; _autoArmed=true;
112
+ const once=ev=>{ if(ev && ev.target && ev.target.closest && ev.target.closest('#play')) return;
113
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
114
+ _autoArmed=false; if(!playing) _autoStart(); };
115
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
116
+ const wx=()=>{ if(!playing) _autoStart(); };
117
+ if(window.WeixinJSBridge && WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},wx);
118
+ }
119
+ document.addEventListener('WeixinJSBridgeReady',()=>{ if(master && !playing) _autoStart(); },false);
120
+ function _whenReady(go){
121
+ if(master.readyState>=3){ go(); return; }
122
+ let fired=false; const f=()=>{ if(fired) return; fired=true; go(); };
123
+ master.addEventListener('canplay',f,{once:true}); setTimeout(f,1200);
124
+ }
125
+
126
  const esc = s => (s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
127
  const fmt = t => { t=Math.max(0,t); const m=Math.floor(t/60), s=t%60; return m+":"+s.toFixed(2).padStart(5,'0'); };
128
  const skc = s => SKC[s]||'#666';
 
140
  window.EPDATA=window.EPDATA||{};
141
  const done=()=>{ SIG=window.EPDATA[slug]; if(!SIG) return; SIG._fw=null; sizeCanvases(); drawCharts();
142
  updateUI(master?master.currentTime:0);
143
+ if(master && !playing) _whenReady(_autoStart); };
 
144
  if(window.EPDATA[slug]){ done(); return; }
145
  const s=document.createElement('script'); s.id='sigjs'; s.src='epdata/'+slug+'.js';
146
  s.onload=done; s.onerror=()=>{ SIG=null; }; document.body.appendChild(s);
egoSample/mego.html CHANGED
@@ -117,6 +117,31 @@ const AX = {x:"#e0616f", y:"#41c76b", z:"#4f8ff7"};
117
  let cur=-1, vids=[], master=null, playing=false, EP=null, SIG=null, raf=0, syncTimer=0, _toggle=null, _lastCi=-2;
118
  let orbH={yaw:0,pitch:-0.28,drag:0}, orbP={yaw:0,pitch:-0.28,drag:0};
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  const esc = s => (s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
121
  const fmt = t => { t=Math.max(0,t); const m=Math.floor(t/60), s=t%60; return m+":"+s.toFixed(2).padStart(5,'0'); };
122
 
@@ -173,9 +198,7 @@ function loadSig(slug){
173
  if(master && fv>0 && master.currentTime<fv-0.01) setTime(fv); else if(master){ updateUI(master.currentTime||0); blitMain(); }
174
  // start playback only once the master tile can actually play, so the per-frame
175
  // slave sync chases a real timestamp instead of hammering seeks against t=0
176
- const go=()=>{ if(_toggle && !playing) _toggle(); };
177
- if(!master) {} else if(master.readyState>=3) go();
178
- else master.addEventListener('canplay', go, {once:true}); };
179
  if(window.EPDATA[slug]){ done(); return; }
180
  const s=document.createElement('script'); s.id='sigjs'; s.src='epdata/'+slug+'.js';
181
  s.onload=done; s.onerror=()=>{ SIG=null; };
 
117
  let cur=-1, vids=[], master=null, playing=false, EP=null, SIG=null, raf=0, syncTimer=0, _toggle=null, _lastCi=-2;
118
  let orbH={yaw:0,pitch:-0.28,drag:0}, orbP={yaw:0,pitch:-0.28,drag:0};
119
 
120
+ /* autoplay for in-app WebViews (WeChat iOS/Android X5): they ignore preload, so
121
+ canplay never fires until play() is called — don't wait on it forever. If play()
122
+ is refused, roll back to paused and retry on WeixinJSBridgeReady / first tap. */
123
+ let _autoArmed=false;
124
+ function _autoStart(){
125
+ if(!master||!_toggle) return;
126
+ if(!playing) _toggle();
127
+ const m=master; Promise.resolve(m.play()).catch(()=>{ if(playing && master===m) _toggle(); _armRetry(); });
128
+ }
129
+ function _armRetry(){
130
+ if(_autoArmed) return; _autoArmed=true;
131
+ const once=ev=>{ if(ev && ev.target && ev.target.closest && ev.target.closest('#play')) return;
132
+ document.removeEventListener('touchend',once,true); document.removeEventListener('click',once,true);
133
+ _autoArmed=false; if(!playing) _autoStart(); };
134
+ document.addEventListener('touchend',once,true); document.addEventListener('click',once,true);
135
+ const wx=()=>{ if(!playing) _autoStart(); };
136
+ if(window.WeixinJSBridge && WeixinJSBridge.invoke) WeixinJSBridge.invoke('getNetworkType',{},wx);
137
+ }
138
+ document.addEventListener('WeixinJSBridgeReady',()=>{ if(master && !playing) _autoStart(); },false);
139
+ function _whenReady(go){
140
+ if(master.readyState>=3){ go(); return; }
141
+ let fired=false; const f=()=>{ if(fired) return; fired=true; go(); };
142
+ master.addEventListener('canplay',f,{once:true}); setTimeout(f,1200);
143
+ }
144
+
145
  const esc = s => (s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
146
  const fmt = t => { t=Math.max(0,t); const m=Math.floor(t/60), s=t%60; return m+":"+s.toFixed(2).padStart(5,'0'); };
147
 
 
198
  if(master && fv>0 && master.currentTime<fv-0.01) setTime(fv); else if(master){ updateUI(master.currentTime||0); blitMain(); }
199
  // start playback only once the master tile can actually play, so the per-frame
200
  // slave sync chases a real timestamp instead of hammering seeks against t=0
201
+ if(master && !playing) _whenReady(_autoStart); };
 
 
202
  if(window.EPDATA[slug]){ done(); return; }
203
  const s=document.createElement('script'); s.id='sigjs'; s.src='epdata/'+slug+'.js';
204
  s.onload=done; s.onerror=()=>{ SIG=null; };