動画、アニメーションや視差効果を利用したページでつまづいたこと

この記事を読むのにかかる時間 1未満

動画の周りに黒い線が入る

画面をスクショするときには黒い線が消える
→画面が小数点以下のpx数をとっていて、勝手に黒い線を表示してしまっている可能性がある
・動画の比率が原因であることが多いが、それでも改善されなかった
・そのため、動画のafter要素に1pxの色付きの線を重ねて、黒い線が目立たないように対応した。

video要素にafter要素は適用したいがうまくいかない

video要素にはafter要素は使用できない

動画の読み込みを検知して処理を開始する

canplaythrough イベントを使用することで、動画の読み込みが完了したことを検知します。

body.no-scroll {
    overflow: hidden;
}
<script>
  // 初期状態でスクロールを無効にする
  document.body.classList.add('no-scroll');

  // 動画要素を取得
  var video = document.getElementById('myVideo');

  // 動画が完全に読み込まれたときの処理
  video.addEventListener('canplaythrough', function() {
      // スクロールを有効にする
      document.body.classList.remove('no-scroll');
  });
</script>

動画がある箇所に入ったときに動画を再生する

<script>
  // 動画要素を取得
  const video = document.getElementById('myVideo');

  // IntersectionObserverのオプション
  const options = {
    root: null,  // ビューポートを基準にする
    rootMargin: '0px',  // 余白なし
    threshold: 0.5  // 要素が50%表示されたら反応する
  };

  // コールバック関数
  const callback = (entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        // 要素がビューポート内に入ったら動画を再生
        video.play();
      } else {
        // 要素がビューポート外に出たら動画を停止
        video.pause();
      }
    });
  };

  // IntersectionObserverのインスタンスを作成
  const observer = new IntersectionObserver(callback, options);

  // 動画要素を監視
  observer.observe(video);
</script>

連続したスクロールだと完了の判定がズレる

// デバウンス用の関数
function debounce(func, wait) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, wait);
  };
}

// スクロールが完了したときに呼び出す処理
function onScrollComplete() {
  console.log('スクロールが完了しました。次の処理を実行');
}

// スクロールイベントをデバウンスして処理
window.addEventListener('scroll', debounce(function() {
  if (window.scrollY >= 1000) { // 任意の条件でスクロール位置を確認
    onScrollComplete();
  }
}, 200));  // 200msの待機時間(適宜調整)

scrollToでページの位置を操作してもその通りに動かない

ブラウザのスクロール復元機能が問題になっている場合は、以下のコードを追加して、ブラウザがスクロール位置を復元しないようにするとうまくいく。

if ('scrollRestoration' in history) {
    history.scrollRestoration = 'manual';
}

window.addEventListener('load', function () {
    window.scrollTo(0, 0);
});

スクロールが確実に終わったら処理を開始するようにしたい

setTimeoutを使うことでスクロール停止を待つことができる理由は、スクロールイベントが連続して発生する性質にあります。setTimeoutの遅延を使って、スクロールが続いていない(スクロールイベントが一定時間発生しない)状態を検出するためにタイミングを調整しています。

let isScrolling;  // タイマー用の変数
let flag = false; // フラグの初期状態

window.addEventListener('scroll', function () {
    // スクロール中は flag を false にする
    flag = false;

    // 前回のタイマーをクリア
    clearTimeout(isScrolling);

    // スクロール停止を待つ (300ms程度)
    isScrolling = setTimeout(function() {
        // スクロール停止後にフラグを true にする
        flag = true;
        console.log('スクロールが完了しました: flag =', flag);

        // スクロール距離が 100 を超え、かつ flag が true の場合に処理を実行
        if (window.scrollY > 100 && flag) {
            // 条件を満たしたときに実行する処理
            console.log('スクロールが100を超え、かつスクロールが完了しました');
            executeAfterScroll();
        }
    }, 300);  // スクロールが終わったと見なす時間(ms)
});

function executeAfterScroll() {
    // スクロール完了後の処理をここに書く
    console.log('スクロール完了後の処理を実行');
}

scrollbar-gutter:stableとoverflow-y:hiddenを用いて、overflow-y:hiddenによるスクロールバー非表示の対策をしている。position:fixedを与えたナビゲーションをright:0で固定しているが、scrollbar-gutter:stableの下に重なって見えなくなってしまった。

ブラウザの再ロードをすることでブラウザの描画が更新される。表示の更新はdocumnt.documentElement.offsetWidthを実行。
さらに、以下のコードでナビゲーションの位置を調節することで対応する。

.nav {
  position: fixed;
  top: 0;
  right: calc(var(--scrollbar-width, 0px));
}
function getScrollbarWidth() {
  return window.innerWidth - document.documentElement.clientWidth;
}

document.documentElement.style.setProperty('--scrollbar-width', `${getScrollbarWidth()}px`);