PageSourceSearch

https://lightbox-archive.com/assets/js/sound.js

js lightbox-archive.com collected 2026-09-29 18:26:47 UTC 1,845 bytes, 47 lines download raw bytes

1/*
2  環境音(Media / 03)。
3  デザイン言語では素の <audio> UI は出さず、経過時間と2pxのバーだけで状態を示す。
4  ページアクセス時に自動再生し、ブラウザが音声付き自動再生を拒否した場合だけ
5  最初のユーザー操作で再生を開始する(そのためのボタン等はUIに足さない)。
6*/
7
8function format(seconds) {
9  const s = Math.floor(seconds || 0);
10  return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0');
11}
12
13function initSound(root) {
14  const audio = root.querySelector('audio');
15  if (!audio) return;
16
17  const bar = root.querySelector('[data-progress]');
18  const current = root.querySelector('[data-cur]');
19  const duration = root.querySelector('[data-dur]');
20
21  audio.addEventListener('loadedmetadata', () => {
22    if (duration) duration.textContent = format(audio.duration);
23  });
24
25  audio.addEventListener('timeupdate', () => {
26    if (current) current.textContent = format(audio.currentTime);
27    if (bar && audio.duration) bar.style.width = (audio.currentTime / audio.duration) * 100 + '%';
28  });
29
30  const attempt = audio.play();
31  if (!attempt || typeof attempt.catch !== 'function') return;
32
33  attempt.catch(() => {
34    // 自動再生がブロックされた場合のフォールバック。
35    // 最初の操作(クリック/キー入力/スクロール)で1度だけ再生を試みる。
36    const events = ['pointerdown', 'keydown', 'scroll'];
37    const kick = () => {
38      audio.play().catch(() => {});
39      events.forEach((name) => window.removeEventListener(name, kick));
40    };
41    events.forEach((name) => window.addEventListener(name, kick, { once: true, passive: true }));
42  });
43}
44
45export function initSounds(root = document) {
46  root.querySelectorAll('.sound').forEach(initSound);
47}

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.