Skip to content

Commit 82db53f

Browse files
author
yszhou
committed
update data video
1 parent 73c09b0 commit 82db53f

4 files changed

Lines changed: 197 additions & 28 deletions

File tree

index.html

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ <h1 class="hero-title">
121121
</div>
122122
<div class="video-container">
123123
<div class="video-wrapper">
124-
<video id="demoVideo" class="demo-video" autoplay muted loop playsinline>
124+
<video id="demoVideo" class="demo-video" muted loop playsinline preload="auto">
125125
<source src="videos/real2sim2real.mp4" type="video/mp4">
126126
Your browser does not support the video tag.
127127
</video>
@@ -180,25 +180,40 @@ <h2 class="section-title">SIM1: The Same One as Reality</h2>
180180
<div class="coverflow-track" id="coverflowTrack" role="list">
181181
<article class="coverflow-slide pos-center" data-index="0" role="listitem">
182182
<div class="coverflow-frame">
183-
<video class="coverflow-vid" muted playsinline preload="metadata">
184-
<source src="videos/data_video/zip_1.mp4" type="video/mp4">
185-
</video>
183+
<iframe
184+
id="coverflowYt0"
185+
class="coverflow-yt"
186+
title="SIM1 long-horizon task replay 1"
187+
src="https://www.youtube.com/embed/tsPLa-1Lygw?playsinline=1&amp;rel=0&amp;modestbranding=1&amp;enablejsapi=1&amp;mute=1"
188+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
189+
allowfullscreen
190+
></iframe>
186191
<span class="coverflow-speed-badge">20× Speed</span>
187192
</div>
188193
</article>
189194
<article class="coverflow-slide pos-right" data-index="1" role="listitem">
190195
<div class="coverflow-frame">
191-
<video class="coverflow-vid" muted playsinline preload="metadata">
192-
<source src="videos/data_video/zip_10.mp4" type="video/mp4">
193-
</video>
196+
<iframe
197+
id="coverflowYt1"
198+
class="coverflow-yt"
199+
title="SIM1 long-horizon task replay 2"
200+
src="https://www.youtube.com/embed/LXStHGWHh18?playsinline=1&amp;rel=0&amp;modestbranding=1&amp;enablejsapi=1&amp;mute=1"
201+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
202+
allowfullscreen
203+
></iframe>
194204
<span class="coverflow-speed-badge">20× Speed</span>
195205
</div>
196206
</article>
197207
<article class="coverflow-slide pos-left" data-index="2" role="listitem">
198208
<div class="coverflow-frame">
199-
<video class="coverflow-vid" muted playsinline preload="metadata">
200-
<source src="videos/data_video/zip_3.mp4" type="video/mp4">
201-
</video>
209+
<iframe
210+
id="coverflowYt2"
211+
class="coverflow-yt"
212+
title="SIM1 long-horizon task replay 3"
213+
src="https://www.youtube.com/embed/zesn7aK9sgQ?playsinline=1&amp;rel=0&amp;modestbranding=1&amp;enablejsapi=1&amp;mute=1"
214+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
215+
allowfullscreen
216+
></iframe>
202217
<span class="coverflow-speed-badge">20× Speed</span>
203218
</div>
204219
</article>
@@ -629,7 +644,7 @@ <h2 class="section-title">Stay in the Loop</h2>
629644
'Could not load the PCA 3D viewer (Three.js module failed). Check network/CDN access, or open this site via a local server: python3 -m http.server in the Webpage folder.';
630645
});
631646
</script>
632-
<script src="scripts/kai-coverflow.js?v=3"></script>
647+
<script src="scripts/kai-coverflow.js?v=4"></script>
633648
<script src="script.js?v=8"></script>
634649
<script src="scripts/results-chart.js?v=3"></script>
635650
<script src="scripts/results-chart-2.js?v=3"></script>

script.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,59 @@ if (demoVideo && demoWrapper) {
139139
return `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}`;
140140
}
141141

142+
// First frame as poster (JPEG data URL), then play only when poster is ready AND canplay
143+
let heroPosterReady = false;
144+
let heroCanPlay = false;
145+
function tryHeroAutoplay() {
146+
if (!heroPosterReady || !heroCanPlay) return;
147+
demoVideo.play().catch(() => {});
148+
}
149+
150+
function captureHeroPoster() {
151+
if (heroPosterReady) return true;
152+
try {
153+
const w = demoVideo.videoWidth;
154+
const h = demoVideo.videoHeight;
155+
if (!w || !h) return false;
156+
const canvas = document.createElement('canvas');
157+
canvas.width = w;
158+
canvas.height = h;
159+
canvas.getContext('2d').drawImage(demoVideo, 0, 0);
160+
demoVideo.poster = canvas.toDataURL('image/jpeg', 0.82);
161+
heroPosterReady = true;
162+
demoWrapper.classList.add('demo-poster-ready');
163+
tryHeroAutoplay();
164+
return true;
165+
} catch (e) {
166+
heroPosterReady = true;
167+
demoWrapper.classList.add('demo-poster-ready');
168+
tryHeroAutoplay();
169+
return true;
170+
}
171+
}
172+
173+
demoVideo.addEventListener('loadeddata', function onHeroLoadedData() {
174+
if (captureHeroPoster()) return;
175+
const onSeeked = () => {
176+
demoVideo.removeEventListener('seeked', onSeeked);
177+
captureHeroPoster();
178+
};
179+
demoVideo.addEventListener('seeked', onSeeked);
180+
demoVideo.currentTime = 0;
181+
}, { once: true });
182+
183+
demoVideo.addEventListener('canplay', () => {
184+
heroCanPlay = true;
185+
tryHeroAutoplay();
186+
}, { once: true });
187+
188+
demoVideo.addEventListener('error', () => {
189+
heroPosterReady = true;
190+
demoWrapper.classList.add('demo-poster-ready');
191+
heroCanPlay = true;
192+
tryHeroAutoplay();
193+
});
194+
142195
const progressBar = demoWrapper.querySelector('.progress-bar');
143196
const progressFilled = demoWrapper.querySelector('.progress-filled');
144197
const currentTimeEl = demoWrapper.querySelector('.current-time');
@@ -185,7 +238,6 @@ if (demoVideo && demoWrapper) {
185238
if (e.key === 'ArrowRight') demoVideo.currentTime = Math.min(demoVideo.duration, demoVideo.currentTime + 5);
186239
});
187240

188-
demoVideo.play().catch(() => {});
189241
}
190242

191243
// Chart bar animations on scroll

scripts/kai-coverflow.js

Lines changed: 99 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* Kai0-style coverflow: center large, sides small; left/right click to switch;
3-
* current video ends → auto-advance to next.
3+
* YouTube embeds: play center, pause sides; current video ends → auto-advance.
4+
* Quality is chosen by YouTube’s adaptive stream (ABR).
45
*/
56
(function () {
67
var root = document.getElementById('kaiTaskReel');
@@ -17,6 +18,7 @@
1718
var n = slides.length;
1819
var active = 0;
1920
var prevActive = -1;
21+
var players = [];
2022

2123
function relPos(i) {
2224
return (i - active + n) % n;
@@ -36,13 +38,24 @@
3638

3739
slides.forEach(function (slide, i) {
3840
var v = slide.querySelector('video');
39-
if (!v) return;
41+
if (v) {
42+
if (relPos(i) === 0) {
43+
if (changed) v.currentTime = 0;
44+
var p = v.play();
45+
if (p && typeof p.catch === 'function') p.catch(function () {});
46+
} else {
47+
v.pause();
48+
}
49+
return;
50+
}
51+
52+
var pl = players[i];
53+
if (!pl || typeof pl.playVideo !== 'function') return;
4054
if (relPos(i) === 0) {
41-
if (changed) v.currentTime = 0;
42-
var p = v.play();
43-
if (p && typeof p.catch === 'function') p.catch(function () {});
55+
if (changed && typeof pl.seekTo === 'function') pl.seekTo(0, true);
56+
pl.playVideo();
4457
} else {
45-
v.pause();
58+
pl.pauseVideo();
4659
}
4760
});
4861
}
@@ -55,15 +68,6 @@
5568
if (hitLeft) hitLeft.addEventListener('click', function () { go(-1); });
5669
if (hitRight) hitRight.addEventListener('click', function () { go(1); });
5770

58-
slides.forEach(function (slide, i) {
59-
var v = slide.querySelector('video');
60-
if (!v) return;
61-
v.addEventListener('ended', function () {
62-
if (active !== i) return;
63-
go(1);
64-
});
65-
});
66-
6771
root.setAttribute('tabindex', '-1');
6872
root.addEventListener('keydown', function (e) {
6973
if (e.key === 'ArrowLeft') {
@@ -118,6 +122,12 @@
118122
{ passive: true }
119123
);
120124

125+
function pauseAllYoutube() {
126+
players.forEach(function (pl) {
127+
if (pl && typeof pl.pauseVideo === 'function') pl.pauseVideo();
128+
});
129+
}
130+
121131
if ('IntersectionObserver' in window && viewport) {
122132
var io = new IntersectionObserver(
123133
function (entries) {
@@ -129,6 +139,7 @@
129139
var v = slide.querySelector('video');
130140
if (v) v.pause();
131141
});
142+
pauseAllYoutube();
132143
}
133144
});
134145
},
@@ -137,5 +148,77 @@
137148
io.observe(viewport);
138149
}
139150

140-
applyLayout();
151+
function bindNativeVideos() {
152+
slides.forEach(function (slide, i) {
153+
var v = slide.querySelector('video');
154+
if (!v) return;
155+
v.addEventListener('ended', function () {
156+
if (active !== i) return;
157+
go(1);
158+
});
159+
});
160+
}
161+
162+
function initYoutubePlayers() {
163+
if (typeof YT === 'undefined' || typeof YT.Player === 'undefined') return;
164+
165+
var ytItems = [];
166+
slides.forEach(function (slide, i) {
167+
var iframe = slide.querySelector('.coverflow-yt');
168+
if (iframe && iframe.id) ytItems.push({ index: i, id: iframe.id });
169+
});
170+
171+
if (!ytItems.length) {
172+
applyLayout();
173+
return;
174+
}
175+
176+
var ready = 0;
177+
var total = ytItems.length;
178+
179+
ytItems.forEach(function (item) {
180+
var i = item.index;
181+
players[i] = new YT.Player(item.id, {
182+
events: {
183+
onStateChange: function (e) {
184+
if (typeof YT === 'undefined' || !YT.PlayerState) return;
185+
if (e.data === YT.PlayerState.ENDED && active === i) go(1);
186+
},
187+
onReady: function () {
188+
ready += 1;
189+
if (ready === total) applyLayout();
190+
}
191+
}
192+
});
193+
});
194+
}
195+
196+
var hasYoutube = root.querySelector('.coverflow-yt');
197+
if (hasYoutube) {
198+
slides.forEach(function (slide) {
199+
var iframe = slide.querySelector('.coverflow-yt');
200+
if (!iframe || !iframe.src) return;
201+
try {
202+
var u = new URL(iframe.src);
203+
u.searchParams.set('origin', window.location.origin);
204+
iframe.src = u.toString();
205+
} catch (e) {}
206+
});
207+
applyLayout();
208+
window.onYouTubeIframeAPIReady = function () {
209+
initYoutubePlayers();
210+
};
211+
if (typeof YT !== 'undefined' && YT.Player) {
212+
initYoutubePlayers();
213+
} else {
214+
var tag = document.createElement('script');
215+
tag.src = 'https://www.youtube.com/iframe_api';
216+
var firstScriptTag = document.getElementsByTagName('script')[0];
217+
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
218+
}
219+
} else {
220+
bindNativeVideos();
221+
applyLayout();
222+
}
141223
})();
224+

styles.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,16 @@ body.theme-light .citation-copy-btn:hover {
831831
object-fit: cover;
832832
}
833833

834+
/* Hide video until first-frame poster is captured (avoids black flash while large file buffers) */
835+
.video-wrapper:not(.demo-poster-ready) .demo-video {
836+
opacity: 0;
837+
}
838+
839+
.video-wrapper.demo-poster-ready .demo-video {
840+
opacity: 1;
841+
transition: opacity 0.28s ease;
842+
}
843+
834844
/* ── Unified Video Player Controls ── */
835845

836846
/* Center play/pause button (appears on hover or when paused) */
@@ -1270,6 +1280,15 @@ body.theme-light .citation-copy-btn:hover {
12701280
object-fit: cover;
12711281
}
12721282

1283+
.coverflow-yt {
1284+
position: absolute;
1285+
inset: 0;
1286+
width: 100%;
1287+
height: 100%;
1288+
border: 0;
1289+
display: block;
1290+
}
1291+
12731292
.coverflow-speed-badge {
12741293
position: absolute;
12751294
left: 0.65rem;

0 commit comments

Comments
 (0)