1import { scenes, motion } from './scene-data.js'; 2import { createLiquid } from './liquid.js'; 3 4const root = document.querySelector('.discover'); 5const continuousScroll = false; 6const find = selector => root.querySelector(selector); 7const stage = find('.discover__stage'); 8const reduced = matchMedia('(prefers-reduced-motion: reduce)'); 9const mobileVideo = matchMedia('(max-width: 768px)'); 10const canvas = find('canvas'), fallback = find('.discover__fallback'); 11const pool = find('.discover__media-pool'); 12const previews = root.querySelectorAll('[data-offset]'); 13const videos = new Map(), posters = new Map(), pending = new Map(); 14const cancelLoad = new WeakMap(); 15let renderer, activeMedia, transition, tween, current = 0, busy = false, epoch = 0; 16let inView = false, pinned = false, frame = 0, trigger; 17let consumed = false, lastWheel = 0, wheelTotal = 0, touch = null; 18let pinPosition = 0, previousScroll = window.scrollY; 19let desiredIndex = 0; 20let autoplayTimer = 0; 21const autoplayDelay = 5000; 22const debugMode = new URLSearchParams(window.location.search).has('discover-video-debug'); 23const debugHistory = []; 24const debugPanel = debugMode ? document.createElement('pre') : null; 25 26if (debugPanel) { 27 debugPanel.className = 'discover__debug'; 28 debugPanel.setAttribute('aria-live', 'polite'); 29 root.append(debugPanel); 30} 31function debug(message) { 32 if (!debugPanel) return; 33 const stamp = new Date().toLocaleTimeString('ja-JP', { hour12: false }); 34 debugHistory.unshift(`${stamp} ${message}`); 35 debugPanel.textContent = debugHistory.slice(0, 7).join('\n'); 36 console.info(`[Discover video] ${message}`); 37} 38 39// SPã§ã¯åç»âWebGLãã¯ã¹ãã£è»¢éãè¡ããªããåç»è¦ç´ ããã®ã¾ã¾è¡¨ç¤ºãã 40// CSSã®ãã§ã¼ãã§åãæ¿ãããã¨ã§GPUè² è·ã¨æç»ã«ã¼ããæããã 41function configureRenderer() { 42 if (mobileVideo.matches || reduced.matches) { 43 renderer = null; 44 root.classList.remove('is-webgl'); 45 return; 46 } 47 if (!renderer) { 48 try { renderer = createLiquid(canvas, motion); } catch { renderer = null; } 49 } 50 root.classList.toggle('is-webgl', !!renderer); 51} 52 53const portal = find('.discover__portal'), cursor = find('.discover__cursor'); 54const pointerQuery = matchMedia('(hover: hover) and (pointer: fine)'); 55function hideCursor() { 56 root.classList.remove('is-pointer'); 57} 58function moveCursor(event) { 59 if (!pointerQuery.matches || event.pointerType === 'touch') { hideCursor(); return; } 60 // ã«ã¼ã½ã«ã®é ç½®å ã¨åãstageãåºæºã«ããpinåå¾ã§ãè¦åºãã®é«ãã座æ¨ã¸æ··ãããªãããã«ããã 61 const rect = stage.getBoundingClientRect(); 62 const x = event.clientX - rect.left, y = event.clientY - rect.top; 63 gsap.set(cursor, { x, y }); 64 root.classList.add('is-pointer'); 65} 66portal.addEventListener('pointerenter', moveCursor); 67portal.addEventListener('pointermove', moveCursor); 68portal.addEventListener('pointerleave', hideCursor); 69window.addEventListener('blur', hideCursor); 70root.addEventListener('keydown', hideCursor); 71pointerQuery.addEventListener('change', hideCursor); 72reduced.addEventListener('change', hideCursor); 73 74// ä¸ä¸ä¸¡æ¹åã¨ã4辺ã®å¤ãæãã¦è£éããéæ¹åã®ã¯ã¤ããå³æè¡¨ç¤ºã«ãªããªãããã«ããã 75previews.forEach(preview => preview.append(preview.querySelector('img').cloneNode())); 76function animatePreviews(target, direction) { 77 previews.forEach(preview => { 78 const [oldImage, newImage] = preview.querySelectorAll('img'); 79 const index = (target + Number(preview.dataset.offset) + scenes.length) % scenes.length; 80 const closed = Number(preview.dataset.offset) < 0 ? 'inset(0% 0% 100% 0%)' : 'inset(100% 0% 0% 0%)'; 81 newImage.src = scenes[index].poster; 82 gsap.set(oldImage, { zIndex: direction > 0 ? 2 : 1, clipPath:'inset(0% 0% 0% 0%)' }); 83 gsap.set(newImage, { zIndex: direction > 0 ? 1 : 2, clipPath: direction > 0 ? 'inset(0% 0% 0% 0%)' : closed }); 84 gsap.to(direction > 0 ? oldImage : newImage, { clipPath: direction > 0 ? closed : 'inset(0% 0% 0% 0%)', duration:reduced.matches ? 0 : .7, delay:reduced.matches ? 0 : .08, ease:'power4.inOut' }); 85 }); 86} 87function updateInfo(updatePreviews = true) { 88 const scene = scenes[current]; 89 find('.discover__title').textContent = scene.title; 90 find('.discover__name').textContent = scene.ja; 91 find('.discover__lead').textContent = scene.lead; 92 find('.discover__portal').href = scene.href; 93 find('.discover__portal').setAttribute('aria-label', `${scene.title}ã®ãã¼ã¸ã¸`); 94 find('.discover__counter').textContent = `${String(current + 1).padStart(2, '0')}/06`; 95 find('.discover__status').textContent = `${current + 1} / 6 ${scene.title}`; 96 root.dataset.index = String(current); 97 if (updatePreviews) previews.forEach(preview => { 98 // ãµã ãã¤ã«ã¯ç«¯ã®å ã鏿ã§ãããã循ç°è¡¨ç¤ºããã¹ã¯ãã¼ã«æ¬ä½ã¯ç«¯ã§éåºããã 99 const next = (current + Number(preview.dataset.offset) + scenes.length) % scenes.length; 100 preview.querySelectorAll('img').forEach(image => { gsap.killTweensOf(image); image.src = scenes[next].poster; gsap.set(image,{clipPath:'inset(0% 0% 0% 0%)',zIndex:0}); }); 101 preview.dataset.target = String(next); 102 preview.setAttribute('aria-label', `${scenes[next].title}ã表示`); 103 }); 104} 105previews.forEach(preview => preview.addEventListener('click', () => { 106 goTo(Number(preview.dataset.target), Math.sign(Number(preview.dataset.offset))); 107})); 108function poster(index) { 109 if (!posters.has(index)) { 110 posters.set(index, new Promise((resolve, reject) => { 111 const image = new Image(); 112 image.onload = () => resolve(image); 113 image.onerror = () => reject(new Error(`ãã¹ã¿ã¼ãèªã¿è¾¼ãã¾ãã: ${scenes[index].poster}`)); 114 image.src = scenes[index].poster; 115 })); 116 } 117 return posters.get(index); 118} 119// åãæ¶ããå¤ãèªè¾¼ã®å®äºéç¥ããåé²å ¥å¾ã®åãæ½è¨ã®åç»ãæ¶ããªãããã«ããã 120function unload(index, video) { 121 cancelLoad.get(video)?.(); 122 video.pause(); video.removeAttribute('src'); video.load(); video.remove(); 123 if (videos.get(index) === video) videos.delete(index); 124} 125function trimVideos() { 126 // iPhoneã§ã¯åæ¿ç´å¾ã«æ¬¡ã®åç»ãç ´æ£ã»åçæããã¨èªè¾¼ãç«¶åããããã 127 // ç¾å¨ã¨é²è¡æ¹åã®æ¬¡ã®1æ¬ã ããä¿æãã3æ¬ä»¥ä¸ã®ãã³ã¼ãã¼ç¢ºä¿ã¯é¿ããã 128 const retained = mobileVideo.matches ? [current, current + 1] : [current]; 129 const keep = new Set(retained.filter(index => index >= 0 && index < scenes.length)); 130 if (transition) { 131 keep.add(transition.from); 132 keep.add(transition.to); 133 } 134 videos.forEach((video, index) => { if (!keep.has(index)) unload(index, video); }); 135} 136function resumeVideo(video) { 137 if (!(video instanceof HTMLVideoElement)) return; 138 // iOS Safariã§ã¯ãDOMç§»åå¾ã«å±æ§ã¨play()ã®ä¸¡æ¹ãæç¤ºããªã㨠139 // æåã®ãã¬ã¼ã ã§åæ¢ãã端æ«ãããã 140 video.muted = true; 141 video.defaultMuted = true; 142 video.autoplay = true;
143 video.playsInline = true; 144 video.setAttribute('muted', ''); 145 video.setAttribute('autoplay', ''); 146 video.setAttribute('playsinline', ''); 147 if (inView && !document.hidden && !reduced.matches && video.isConnected) { 148 video.play() 149 .then(() => debug(`play OK #${Number(video.dataset.journeyIndex) + 1} / readyState:${video.readyState}`)) 150 .catch(error => debug(`play NG #${Number(video.dataset.journeyIndex) + 1} / ${error.name}: ${error.message}`)); 151 } 152} 153function syncVideos() { 154 videos.forEach((video, index) => { 155 // å èªã¿ç¨ã®åç»ã¯åçããªããç¾å¨è¡¨ç¤ºä¸ã¨ãé·ç§»ä¸ã®2æ¬ã ããåçããã 156 const shouldPlay = inView 157 && !document.hidden 158 && !reduced.matches 159 && (index === current || Boolean(transition && (index === transition.from || index === transition.to))); 160 if (shouldPlay) resumeVideo(video); 161 else video.pause(); 162 }); 163} 164function preloadNext() { 165 // PCã¯å¾æ¥ã©ããWebGLã®åæ¿æã«ã ãèªã¿è¾¼ããSafari対çã¯SPã«éå®ããã 166 if (!mobileVideo.matches || !inView || reduced.matches) return; 167 const nextIndex = current + 1; 168 if (nextIndex >= scenes.length || videos.has(nextIndex) || pending.has(nextIndex)) return; 169 debug(`preload start #${nextIndex + 1}`); 170 media(nextIndex).then(() => { 171 // å èªã¿å®äºå¾ã次ã®åç»ã¯åæ¢ããã¾ã¾ä¿æããã 172 trimVideos(); 173 syncVideos(); 174 debug(`preload ready #${nextIndex + 1}`); 175 }); 176} 177mobileVideo.addEventListener('change', () => { 178 // å転ããªãµã¤ãºã§768pxå¢çãã¾ããã å ´åããä¿æä¸ã®åç»ãä½ãç´ãã 179 configureRenderer(); 180 videos.forEach((video, index) => unload(index, video)); 181 pending.clear(); 182 if (!inView) return; 183 poster(current).then(showFallback).catch(() => {}); 184 media(current).then(next => { 185 if (!transition) { 186 activeMedia = next; 187 syncVideos(); 188 draw(); 189 requestFrame(); 190 } 191 }); 192}); 193async function media(index) { 194 const still = await poster(index); 195 if (reduced.matches) return still; 196 if (pending.has(index)) return pending.get(index); 197 if (videos.has(index)) return videos.get(index); 198 const promise = new Promise(resolve => { 199 const video = document.createElement('video'); 200 video.muted = true; video.defaultMuted = true; video.autoplay = true; video.loop = true; video.playsInline = true; video.preload = 'metadata'; 201 video.setAttribute('muted', ''); video.setAttribute('autoplay', ''); video.setAttribute('playsinline', ''); 202 video.dataset.journeyIndex = String(index); 203 video.poster = scenes[index].poster; pool.append(video); videos.set(index, video); 204 // é 信失æãçãã¼ã¿ç°å¢ã§ãpinå ã«éãè¾¼ããªããããæéå ã«ãã¹ã¿ã¼è¡¨ç¤ºã¸æ»ãã 205 const timeout = setTimeout(() => finish(false), 6000); 206 let finished = false; 207 function finish(ok) { 208 if (finished) return; 209 finished = true; cancelLoad.delete(video); 210 if (pending.get(index) === promise) pending.delete(index); 211 clearTimeout(timeout); video.onloadeddata = null; video.onerror = null; 212 if (!ok) { unload(index, video); resolve(still); } 213 else { syncVideos(); resolve(video); } 214 } 215 cancelLoad.set(video, () => finish(false)); 216 let triedPcFallback = false; 217 const source = mobileVideo.matches ? scenes[index].videoSp : scenes[index].video; 218 video.onloadeddata = () => { 219 debug(`loadeddata #${index + 1} / ${source.split('/').pop()} / readyState:${video.readyState}`); 220 finish(true); 221 }; 222 video.onerror = () => { 223 debug(`load NG #${index + 1} / ${video.currentSrc || source}`); 224 // SPç´ æãã¾ã æªé ç½®ã§ãJourneyããã¹ã¿ã¼è¡¨ç¤ºã«ãããPCåç»ã使ãã 225 if (source === scenes[index].videoSp && !triedPcFallback) { 226 triedPcFallback = true; 227 debug(`PCã¸ãã©ã¼ã«ãã㯠#${index + 1} / ${scenes[index].video.split('/').pop()}`); 228 video.src = scenes[index].video; 229 video.load(); 230 return; 231 } 232 finish(false); 233 }; 234 debug(`load start #${index + 1} / ${source}`); 235 video.src = source; 236 }); 237 pending.set(index, promise); 238 return promise; 239} 240function showFallback(source) { 241 fallback.replaceChildren(source); 242 if (source instanceof HTMLVideoElement) { 243 debug(`display #${Number(source.dataset.journeyIndex) + 1} / ${source.currentSrc || source.src}`); 244 requestAnimationFrame(() => resumeVideo(source)); 245 } 246} 247function restartFallbackTransition() { 248 fallback.classList.remove('is-transitioning'); 249 // åä¸ã¯ã©ã¹ãç¶ãã¦ä»ä¸ããå ´åããåã¹ã©ã¤ãã§ã¢ãã¡ã¼ã·ã§ã³ãåéããã 250 void fallback.offsetWidth; 251 fallback.classList.add('is-transitioning'); 252} 253function draw() { 254 if (!activeMedia || !renderer) return; 255 const a = transition?.a || activeMedia, b = transition?.b || activeMedia; 256 const p = transition?.progress || 0; 257 try { renderer.draw(a, b, p, transition?.direction || 1); }
258 catch { renderer = null; root.classList.remove('is-webgl'); showFallback(p < .5 ? a : b); } 259} 260function render() { 261 frame = 0; 262 if (!inView || document.hidden) return; 263 draw(); 264 if (renderer && (transition || activeMedia instanceof HTMLVideoElement)) requestFrame(); 265} 266function requestFrame() { if (renderer && !frame && inView && !document.hidden) frame = requestAnimationFrame(render); } 267function setBusy(value) { 268 busy = value; root.setAttribute('aria-busy', String(value)); 269 // é·ç§»ä¸ããã¼ãã¼ããã©ã¼ã«ã¹ãä¿ã¡ã追å å ¥åã¯goToã®busyå¤å®ã§æããã 270 previews.forEach(preview => { preview.setAttribute('aria-disabled', String(value)); }); 271 if (value) clearAutoplay(); 272 else scheduleAutoplay(); 273} 274function clearAutoplay() { 275 if (!autoplayTimer) return; 276 window.clearTimeout(autoplayTimer); 277 autoplayTimer = 0; 278} 279function scheduleAutoplay() { 280 clearAutoplay(); 281 if (reduced.matches || !inView || document.hidden || busy) return; 282 autoplayTimer = window.setTimeout(() => { 283 autoplayTimer = 0; 284 if (!busy && inView && !document.hidden) { 285 goTo((current + 1) % scenes.length, 1); 286 } 287 }, autoplayDelay); 288} 289function syncSceneFromScroll(progress) { 290 if (typeof progress === 'number') desiredIndex = Math.min(scenes.length - 1, Math.floor(progress * scenes.length)); 291 if (continuousScroll && !busy && desiredIndex !== current) goTo(desiredIndex, Math.sign(desiredIndex - current) || 1); 292} 293async function show(index) { 294 const token = ++epoch; 295 tween?.kill(); transition = null; activeMedia = null; setBusy(true); 296 current = index; updateInfo(); root.classList.remove('is-switching'); fallback.classList.remove('is-transitioning'); 297 trimVideos(); 298 try { 299 const next = await media(index); 300 // ã¹ã¯ãã¼ã«ãã¼æä½ãåé²å ¥ã§èªã¿è¾¼ã¿å ãå¤ãã£ã¦ããå¤ãçµæã表示ããªãã 301 if (token !== epoch) return; 302 activeMedia = next; 303 if (!renderer) showFallback(next); 304 syncVideos(); requestFrame(); 305 } catch (error) { 306 find('.discover__status').textContent = 'æ åãèªã¿è¾¼ãã¾ããã§ããããªã³ã¯ããæ½è¨ãã¼ã¸ã¸é²ãã¾ãã'; 307 console.warn(error); 308 } finally { 309 if (token === epoch) { 310 setBusy(false); 311 trimVideos(); 312 preloadNext(); 313 } 314 } 315} 316async function step(direction) { 317 if (busy) return; 318 const target = current + direction; 319 if (target < 0 || target >= scenes.length) { leave(direction); return; } 320 return goTo(target, direction); 321} 322async function goTo(target, direction) { 323 if (busy || target === current) return; 324 const token = ++epoch; 325 setBusy(true); 326 try { 327 const next = await media(target); 328 if (token !== epoch) return; 329 transition = { from: current, to: target, a: activeMedia || next, b: next, progress: 0, direction }; 330 animatePreviews(target, direction); 331 root.classList.add('is-switching'); 332 if (!renderer) restartFallbackTransition(); 333 let swapped = false; 334 tween = gsap.to(transition, { 335 progress: 1, duration: reduced.matches ? 0 : motion.duration, ease: t => t * t * (3 - 2 * t), 336 onUpdate() { 337 if (transition.progress >= .5 && !swapped) { 338 swapped = true; current = target; updateInfo(false); root.classList.remove('is-switching'); 339 if (!renderer) showFallback(next); 340 } 341 draw(); 342 }, 343 onComplete() { 344 current = target; activeMedia = next; transition = null; 345 updateInfo(); root.classList.remove('is-switching'); 346 if (!renderer) showFallback(next); 347 fallback.classList.remove('is-transitioning'); 348 setBusy(false); trimVideos(); syncVideos(); preloadNext(); requestFrame(); 349 }, 350 }); 351 syncVideos(); requestFrame(); 352 } catch (error) { 353 setBusy(false); trimVideos(); find('.discover__status').textContent = 'èªã¿è¾¼ã¿ãå試è¡ãã¦ãã ããã'; 354 console.warn(error); 355 } 356} 357function enter(direction, self) { 358 pinned = true; root.dataset.pinned = 'true'; 359 pinPosition = Math.max(self.start + 1, Math.min(window.scrollY, self.end - 1)); 360 // å°çããæä½ã¯åºå®ã®å°å ¥ã¨ãã¦æ¶è²»ããæ¬¡ã®æç¢ºãªæä½ã§é常ã¹ã¯ãã¼ã«ã¸æ»ãã 361 consumed = true; lastWheel = performance.now(); wheelTotal = 0; 362 if (touch) touch.used = true; 363} 364function leave(direction) { 365 if (continuousScroll) return; 366 pinned = false;
366 root.dataset.pinned = 'false'; consumed = true; 367 // pinã®äºç´ä½ç½ã ããæ¶åããã端ç¹ã®æç»ä½ç½®ã¯åããªã®ã§ãç¶ãå®å ¥åã§é常ã¹ã¯ãã¼ã«ã¸åºãããã 368 // å ¥å ´æã®èªåã¹ãããã«ã¯ä½¿ç¨ããªãã 369 moveScroll(direction > 0 ? trigger.end + 1 : trigger.start - 1); 370} 371function moveScroll(position) { 372 previousScroll = position; 373 if (window.kawakyuSmoothScroll) { 374 window.kawakyuSmoothScroll.scrollTo(position, { immediate: true, force: true }); 375 } else { 376 trigger.scroll(position); 377 } 378 ScrollTrigger.update(); 379} 380function entryPosition(from, to) { 381 if (from <= trigger.start && to > trigger.start) return trigger.start + 1; 382 if (from >= trigger.end && to < trigger.end) return trigger.end - 1; 383 return null; 384} 385// ä¸åº¦ã®å ¥åãpinåºéå ¨ä½ãè¶ããå ´åããå®éã«æ¨ªåã£ãå ¥å£ã§æ¢ããã 386// ã¿ããã®æ £æ§ã¯touchmoveã®ãã£ã³ã»ã«ã ãã§ã¯æ¢ã¾ããªããããpinä¸ã®åº§æ¨ãä¿æããã 387window.addEventListener('scroll', () => { 388 if (continuousScroll) return; 389 if (!trigger) return; 390 const position = pinned ? pinPosition : entryPosition(previousScroll, window.scrollY); 391 if (position !== null && Math.abs(window.scrollY - position) > .5) moveScroll(position); 392 previousScroll = window.scrollY; 393}, { passive: true }); 394// æç¤ºçãªãã¼ã¸å ãªã³ã¯ã¯é常ã®ç§»åãåªå ããpinã®ééè£æ£ã«æã¾ããªãã 395document.addEventListener('click', event => { 396 const link = event.target.closest('a[href^="#"]'); 397 const target = link && document.getElementById(link.hash.slice(1)); 398 if (!target || root.contains(target)) return; 399 pinned = false; root.dataset.pinned = 'false'; 400 previousScroll = window.scrollY + target.getBoundingClientRect().top; 401}); 402function atEdge(direction) { return direction < 0 ? current === 0 : current === scenes.length - 1; } 403window.addEventListener('wheel', event => { 404 if (document.documentElement.classList.contains('is-hamburger-menu-open')) return; 405 if (continuousScroll) return; 406 if (event.ctrlKey || Math.abs(event.deltaX) > Math.abs(event.deltaY)) return; 407 const delta = event.deltaY * (event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? innerHeight : 1); 408 const entry = trigger && !pinned ? entryPosition(window.scrollY, window.scrollY + delta) : null; 409 if (entry !== null && event.cancelable) { 410 event.preventDefault(); moveScroll(entry); return; 411 } 412 const now = performance.now(); 413 if (now - lastWheel > 240 && !busy) { consumed = false; wheelTotal = 0; } 414 lastWheel = now; 415 if (!pinned || !event.deltaY) return;
416 if (!busy && !consumed) { leave(Math.sign(event.deltaY)); return; } 417 if (event.cancelable) event.preventDefault(); 418}, { passive: false }); 419window.addEventListener('touchstart', event => { 420 if (document.documentElement.classList.contains('is-hamburger-menu-open')) { touch = null; return; } 421 touch = event.touches.length === 1 ? { x: event.touches[0].clientX, y: event.touches[0].clientY, used: busy, blocked: false } : null; 422}, { passive: true }); 423window.addEventListener('touchmove', event => { 424 if (document.documentElement.classList.contains('is-hamburger-menu-open')) return; 425 if (continuousScroll) return; 426 if (!touch || event.touches.length !== 1 || !pinned) return; 427 const delta = touch.y - event.touches[0].clientY; 428 if (Math.abs(event.touches[0].clientX - touch.x) > Math.abs(delta)) return; 429 if (!touch.used && !busy && !touch.blocked) { 430 if (Math.abs(delta) > 4) { touch.used = true; leave(Math.sign(delta)); } 431 return; 432 } 433 if (event.cancelable) { event.preventDefault(); touch.blocked = true; } 434}, { passive: false }); 435window.addEventListener('touchend', () => { touch = null; }, { passive: true }); 436window.addEventListener('touchcancel', () => { touch = null; }, { passive: true }); 437window.addEventListener('keydown', event => { 438 if (continuousScroll) return; 439 if (!pinned || event.altKey || event.ctrlKey || event.metaKey || /INPUT|TEXTAREA|SELECT/.test(event.target.tagName)) return; 440 // ãµã ãã¤ã«ã®Spaceã¯buttonæ¬æ¥ã®é¸ææä½ã¸æ¸¡ããæ¬¡ã¹ã©ã¤ãæä½ã¨éããªãã 441 if (event.key === ' ' && event.target.closest('button')) return; 442 const direction = ['ArrowDown', 'PageDown', ' '].includes(event.key) ? (event.shiftKey ? -1 : 1) 443 : ['ArrowUp', 'PageUp'].includes(event.key) ? -1 : 0; 444 if (!direction) return; 445 event.preventDefault(); 446 if (busy || event.repeat) return; 447 leave(direction); 448 window.scrollBy(0, direction * 80); 449}); 450 451configureRenderer(); 452canvas.addEventListener('webglcontextlost', event => { 453 event.preventDefault(); renderer = null; root.classList.remove('is-webgl'); 454 if (activeMedia) showFallback(transition && transition.progress >= .5 ? transition.b : activeMedia); 455}); 456new IntersectionObserver(entries => { 457 inView = entries[0].isIntersecting; 458 syncVideos(); requestFrame(); scheduleAutoplay(); 459}, { threshold: 0 }).observe(root); 460document.addEventListener('visibilitychange', () => { syncVideos(); requestFrame(); scheduleAutoplay(); }); 461new ResizeObserver(() => { draw(); requestFrame(); }).observe(root); 462reduced.addEventListener('change', () => { configureRenderer(); show(current); }); 463 464gsap.registerPlugin(ScrollTrigger); 465// Journeyã¸å ¥ãéã ãçãåºå®ããæ¬¡ã®æç¢ºãªæä½ã§ä¸ä¸ã®ãã¼ã¸ã»ã¯ã·ã§ã³ã¸é²ããã 466trigger = ScrollTrigger.create({ 467 id: 'shi142-discover', trigger: stage, pin: stage, start: 'top top', end: '+=120', 468 onEnter: self => enter(1, self), onEnterBack: self => enter(-1, self), 469 onLeave: () => { pinned = false; root.dataset.pinned = 'false'; }, 470 onLeaveBack: () => { pinned = false; root.dataset.pinned = 'false'; }, 471}); 472updateInfo(); show(current); 473document.fonts.ready.then(() => ScrollTrigger.refresh());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.