本文へジャンプ

GSAPとJSを使ったスクロールに合わせて動画にエフェクトをつける演出の作り方

Posted by OYM

日々の業務をこなしながら、お酒をたくさん飲みたいな〜と思うOYMです。
九州出身ということもあり、割とお酒は強い方だと自負しておりますが、毎回、会社の飲み会では飲みすぎないかとヒヤヒヤしております...。

アニメーション演出のロジックと実装が楽しい

最近、マークアップをする上で、デザイナーからの演出の提案も多く受け、アニメーション多彩なサイト制作を行うことも増えてきました。
そんな中で提案されたものを頭の中でロジックを組んで、実現させた時は最高に気持ちがいいです!

そこで、今回は弊社が提供している MONSTER STUDIO乃木坂 のサイトでも実装した、スクロールすると動画にエフェクトがかかる演出を実装したので、JavaScriptのスニペットを紹介していきたいと思います。
この演出も、デザイナーと あぁでもない、こうでもないと論争を繰り広げていました。

まずは実装した画面の動きから

スクロールに合わせて動画にエフェクトをつける演出

簡単に作ったものは、こちらにデモがあります。

スクロールしていくと自動再生された動画が固定されてテクスチャとボカシ表現が強くなりますね。一定量のスクロールをすると次のセクションに移るという仕組みです。

コード解説

今回作成したJSのコードは下記になります。

gsap.registerPlugin(ScrollTrigger);

const targetEl = document.getElementById("p-index__fv");
const glassEl = document.getElementById("glass");
const noiseEl = document.getElementById("noise");

const scrollAnim = () => {
  const targetTmp = targetEl?.getBoundingClientRect();

  if (targetTmp && targetEl && glassEl && noiseEl) {
    const ratio = (-targetTmp.top * 2.5) / targetTmp.height;
    // console.log(ratio)
    glassEl.style.backdropFilter =
      "blur(" + ratio * 20 + "px) saturate(" + (100 - ratio * 100) + "%";
    noiseEl.style.opacity = "" + ratio / 2;
  }
};

if (!(window.matchMedia && window.matchMedia("(max-width: 1140px)").matches)) {

  gsap.to(".js-scroll", {
    scrollTrigger: {
      trigger: ".c-fv__video",
      start: "bottom 100%",
      end: "bottom -100%",
      pin: true
      // markers: true
    },
  });

  window.addEventListener("scroll", scrollAnim);
  // console.log("mount");

}

簡単に説明すると、GSAPというライブラリを用いてセクション間は動画を固定しつつ、セクション間でスクロールすると、徐々に動画にエフェクトがかかる仕組みになっています。
やっていることは複雑なように聞こえますが、仕組みと実装は単純ですので、GSAPのコードは割愛して、"scrollAnim" という関数の中で行われている処理を細かく分解して解説していければなと思います。

DOMの読み込みと事前準備

const targetEl = document.getElementById("p-index__fv");
const glassEl = document.getElementById("glass");
const noiseEl = document.getElementById("noise");

まずはDOMを指定するところからスタート "targetEl"は今回スクロールするときに、固定させる範囲のDOMを指定しています。
今回の実装では "glassEl" と "noiseEl" が動画の全面にあり、動画のDOMにマスクをかけるような仕組みになっています。

図解するとこんな感じです。レイヤー分けして乗せています。

レイヤー分けした図解

const targetTmp = targetEl?.getBoundingClientRect();

この後に行う数式で、取得した動画セクションのスクロールに合わせた処理を行うため、高さや位置を取得できるよう getBoundingClientRect() で値を取得しています。便利なメソッドですねぇ

計算とスタイルの適用

const ratio = -targetTmp.top / targetTmp.height;

ここで計算しているのは、先ほど取得した動画セクションの情報からコンテンツのTOP位置からDOMの高さ情報を除算することで、セクションのスクロール量に対する比率が算出されます。 targetTmp.top をマイナスにすることで出力される値が正値になります。

glassEl.style.backdropFilter = "blur(" + ratio * 20 + "px) saturate(" + (100 - ratio * 100) + "%";
noiseEl.style.opacity = "" + ratio / 2;

コンテンツのスクロール位置を取得できたため、あとはそれに合わせてCSSの値を変更すれば完了です。各レイヤーにCSSを適用し、スクロール量に応じて値が変わるというシンプルな仕組みです。

"glassEl" では、主に動画に対するエフェクトを適用しています。そこで選んだのが"backdrop-filter" です。これはとても便利なCSSで、多くのエフェクトを複数設定することができます。

  • blur:ぼかしのエフェクトです。数値を増やせば増やすほど、ぼけが増します
  • saturate:彩度のエフェクトです。数値が減るほど、モノトーンになります。

blur の計算式にある「* 20」という乗算は、しきい値のため、スクロール量に対して足りないと感じたら個々の調整をするといいです。

saturate 内の計算式は、デフォルトが「100」のため、スクロール量に合わせて減算していく計算式となっています。

その下にある "noiseEl" は、デザインで用意されたノイズの乗ったテクスチャ画像ををスクロールに合わせてふわっと表示させています。
こちらも「2」で除算しているのは、opacityの値が 1になり切らないギリギリの値を調整すべく実際の量の半分が適用されるように調整しております。

この2つが、スクロール量に合わせて変化していくことで、動画にエフェクトをかけているように見えるのです。今回は動画に対して行いましたが、場合によっては背景色を変えたり、セクション間の移動時にトランジション的な用い方もできそうな気がします!

まとめ

今回初めてJavaScriptのコードを解説していきましたが、改めて見直すとリファクタリングできる余地がたくさんあったため、コードを一度書き直しました。第三者に説明するときに、コードを詳しく噛み砕いていくと気づきというのも見つかっていくため、自分なりにもいい経験になりました。

Webエンジニアとしては、実装したコードを誰かに誰かに見て欲しいという思いもあるため、今後もちょっとしたTipsでもプロジェクトで実装した事例を紹介していければと思っています。

まだまだ未熟で日々勉強の身ではありますが、今後いろんな技術や知識を共有できるメンバーをお待ちしております!

Recent Entries
MD EVENT REPORT
What's Hot?