JSとCSSでふわふわと漂う泡のアニメーションを実装する方法【自作&jQueryなし】

最近のWebサイト制作では、アニメーションを取り入れるのが一般的ですよね。

サイトに動きをつけることによってかっこよく見せたり、ユーザーの関心を惹きつけることができます。

今回の記事では、JavaScriptとCSSを使って、画面下から泡がふわふわ浮かび上がっていくアニメーションを実装していきます!

アニメーションの実装には、以下の動画を参考にしました。

この動画をベースに少し改良を加えて、泡のアニメーションを実装していきます。

目次

ふわふわと浮き上がる泡のアニメーションのDEMO

まずはDEMOページで、実装内容を確認してください。

少し下にスクロールすると、泡が湧き上がってくるアニメーションが見つかるかと思います。

DEMOを確認できたら、実際に手順を見ていきましょう。

泡が浮かび上がるアニメーションの作成手順

それでは、HTML, CSS, JavaScriptの順番で作り方を説明していきますね。

それぞれの役割は、以下のようになっています。

  • HTML・・・泡を表示するコンテナを作る。
  • CSS・・・泡の見た目を指定する。
  • JS・・・泡のサイズと発生位置をランダムにして、数秒ごとに泡を生成する。泡を表示するコンテナの位置を監視し、表示画面内に入ったら泡の生成を開始する。コンテナが表示画面外に出ると、泡の生成をストップする。

1.泡を表示するコンテナを指定する

<div class="bubble-background">
<!-- ここに好きなコンテンツを入れる -->
</div>
.bubble-background {
  position: relative;
  background-color: #000;
  height: 100vh;
  width: 100%;
  overflow: hidden;
}

まずは、泡を表示したい場所を指定して、JSで指定できるようにクラスかIDを付けます。

ここでは、bubble-backgroundというクラスを付けました。

CSSは、最低限のスタイルを記載しておきますので、色や大きさ等は自由に調節してください。

泡の色が白なので、background-colorの値は白以外に設定しておいてくださいね。

2.CSSで泡の見た目を作っていく

/* 泡の見た目 */
.bubble {
  position: absolute;
  bottom: -50px;
  background-color: transparent;
  border-radius: 50%;
  pointer-events: none;
  box-shadow: inset 0 0 10px rgba(255,255,255,0.2);
  animation: bubble 8s linear infinite;
}

/* 泡の一番明るい部分を疑似要素で表現 */
.bubble::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  transform: scale(0.25) translate(-70%, -70%);
  background: radial-gradient(rgba(255,255,255, 0.5), transparent);
  border-radius: 50%;
}

/* 泡が上に浮かび上がっていくアニメーション */
@keyframes bubble {
  0% {
    transform: translatY(0);
    opacity: 1;
  }
  99% {
    opacity: 1;
  }
  100% {
    transform: translateY(-100vh);
    opacity: 0;
  }
}

泡のひとつひとつにはbubbleというクラスを付け、見た目を作っていきます。

泡が発生した瞬間は隠しておきたいので、bottom: -50pxを指定。先程作った親要素にoverflow: hiddenを指定しているので、初期状態では見えないようになっています。

泡の本体にはbox-shadowinsetを指定することで、透明感のある泡のような見た目を表現しています。

泡の大きさはJavaScriptでランダムに指定するので、ここでは設定しなくて大丈夫です。

泡の明るい部分は、疑似要素で小さな円を重ねることで実装しています。ここでポイントなのは、radial-gradientの部分ですね。

これは円形のグラデーションを指定できる関数で、中心は半透明の白で、外側にいくにつれ透明になっていくという指定をしています。

明るさを変えたい場合は、0.5となっている箇所を0~1のあいだで指定してください。

最後の@keyframesで、泡が浮かび上がっていくアニメーションを指定しています。

transformYの部分に注目してください。

0%の時点では0、つまり泡は一番下にセットされています。そして100%の時点では-100vhとなっていますね。

これは”泡を上に100vh(画面の高さ分)移動させる”という意味になります。

この値は親要素の高さ・泡をどこまで上げたいかによって変わるので、各自調整してください。

3.JavaScriptで泡を生成する

JavaScriptのコードは少し長いので、いったん全体像を確認したあと、分解して説明していきます。

document.addEventListener('DOMContentLoaded', () => {
  // コンテナを指定
  const section = document.querySelector('.bubble-background');

  // 泡を生成する関数
  const createBubble = () => {
    const bubbleEl = document.createElement('span');
    bubbleEl.className = 'bubble';
    const minSize = 10;
    const maxSize = 50;
    const size = Math.random() * (maxSize + 1 - minSize) + minSize;
    bubbleEl.style.width = `${size}px`;
    bubbleEl.style.height = `${size}px`;
    bubbleEl.style.left = Math.random() * innerWidth + 'px';
    section.appendChild(bubbleEl);

    // 一定時間が経てば泡を消す
    setTimeout(() => {
      bubbleEl.remove();
    }, 8000);
  }

  // 泡の生成を開始するトリガー(初期値はOFF)
  let activeBubble = null;

  // 泡の生成をストップする関数
  const stopBubble = () => {
    clearInterval(activeBubble);
  };

  // Intersection observerに渡すコールバック関数
  const cb = (entries) => {
    entries.forEach(entry => {
      if(entry.isIntersecting) {
        activeBubble = setInterval(createBubble, 300);
      } else {
        stopBubble();
      }
    })
  };

  // Intersection observerに渡すオプション
  const options = {
    rootMargin: "100px 0px"
  }

  // Intersection observerの初期化
  const io = new IntersectionObserver(cb, options);
  io.POLL_INTERVAL = 100; // Polyfill
  io.observe(section);
});

3-0.【下準備】Polyfillを読み込む

ここではIntersection observer APIを使うのですが、対応していないブラウザのためにPolyfillを読み込む必要があります

こちらのページから、intersection-observer.jsをダウンロードするか、ソースをコピーしてください。

以下のようにして、メインのJSファイルよりも先にpolyfillを読み込みます。

  <script src="intersection-observer.js"></script>
  <script src="main.js"></script>
</body>
</html>

これで下準備は完了なので、さっそくコードを見ていきましょう。

3-1.泡を発生させるコンテナを取得する

const section = document.querySelector('.bubble-background');

まずは、泡が生成されるコンテナを取得します。

コンテナにはbubble-backgroundというクラス名を付与したので、querySelectorを使って指定します。

3-2.泡を発生させる関数を定義する

const createBubble = () => {
  const bubbleEl = document.createElement('span'); // spanタグを生成
  bubbleEl.className = 'bubble'; // 泡に'bubble'というクラスを付ける
  const minSize = 10; // 泡の最小サイズを指定
  const maxSize = 50; // 泡の最大サイズを指定
  const size = Math.random() * (maxSize + 1 - minSize) + minSize; // 上で指定した範囲の中からランダムでサイズを設定
  bubbleEl.style.width = `${size}px`; // 取得したランダムなサイズを泡の横幅に指定
  bubbleEl.style.height = `${size}px`; // 取得したランダムなサイズを泡の縦幅に指定
  bubbleEl.style.left = Math.random() * innerWidth + 'px'; // 泡が発生する位置をランダムで指定
  section.appendChild(bubbleEl); // 完成した泡をコンテナに挿入する

  // 一定時間が経てば泡を消す
  setTimeout(() => {
    bubbleEl.remove();
  }, 8000); // 泡を消すまでの時間をミリ秒単位で指定
}

この関数では、ランダムサイズの泡を生成して、一定時間が経てば消すという動作を指定しています。

まずspanタグで泡のHTML要素を生成し、bubbleのクラスを付与。

次にminSizemaxSizeに数値を入れることで、泡の最小・最大サイズを指定できます。

const size = Math.random() * (maxSize + 1 - minSize) + minSize;

この行で、指定した最小・最大サイズの間でランダムな数値をsizeという変数に格納します。この場合だと、10から50の間の数値が入っていることになりますね。

このsizeを泡の横幅・縦幅に指定してあげることで、泡のスタイルが完成します。

bubbleEl.style.left = Math.random() * innerWidth + 'px';

この行では、泡が発生する水平方向の位置をランダムで設定しています。

まずはinnerWidthでビューポートの横幅を取得。

仮にブラウザウィンドウの幅が1440pxとすると、innerWidthの数値は1440となります。

そしてMath.random()関数で0~1440の間でランダムな数値を取得。

この数値をCSSのleftプロパティの値として設定することで、泡の位置をランダムに設定しているというわけですね。

// 一定時間が経てば泡を消す
setTimeout(() => {
  bubbleEl.remove();
}, 8000); // 泡を消すまでの時間をミリ秒単位で指定

生成された泡は上へ上昇していきますが、コンテナにはoverflow: hiddenが指定されているため、コンテナよりも上にいくと見えなくなります。

そうなるとわざわざ泡の要素を残しておく必要はないので、一定時間が経てばDOMから削除しましょう。

setTimeoutを使って、泡を生成してから8秒後に泡を削除するようにします。

この秒数は調整可能なので、自分のプロジェクトに合わせて変えてください。

3-3.コンテナが画面内に入ると泡を発生させる

// Intersection observerに渡すコールバック関数
const cb = (entries) => {
  entries.forEach(entry => {
    if(entry.isIntersecting) { // コンテナが画面内に入っているときの処理
      activeBubble = setInterval(createBubble, 300);
    } else { // コンテナが画面外にあるときの処理
      stopBubble();
    };
  });
};

// Intersection observerに渡すオプション
const options = {
  rootMargin: "100px 0px"
}

// Intersection observerの初期化
const io = new IntersectionObserver(cb, options);
io.POLL_INTERVAL = 100; // Polyfill
io.observe(section);

上で泡を発生させる関数を定義しましたが、この時点では泡は生成されません。

ここではIntersection observer APIを使って、

  • コンテナが画面内に入ったら泡を生成する
  • コンテナが画面外に出たら泡の生成をストップする

というのを実装していきます。

まずIntersection observerはコールバック関数とオプションを渡せるので、それぞれ準備しましょう。

// Intersection observerに渡すコールバック関数
const cb = (entries) => {
  entries.forEach(entry => {
    if(entry.isIntersecting) { // コンテナが画面内に入っているときの処理
      activeBubble = setInterval(createBubble, 300);
    } else { // コンテナが画面外にあるときの処理
      stopBubble();
    };
  });
};

このコールバック関数には、要素が画面内に入ったときの処理と、画面の外に出たときの処理を記述します。

まずコンテナが画面内に入っている時に泡を生成したいので、以下のコードのようになります。

if(entry.isIntersecting) { // コンテナが画面内に入っているときの処理
  activeBubble = setInterval(createBubble, 300);
}

setIntervalを使って、一定秒ごとに泡を生成する関数を呼び出すという処理を行っています。

ここでは300の数値が入っているため、”0.3秒ごとに泡を1つ生成する”という意味になりますね。

この数値を調整することで泡の発生頻度を変えられるので、お好みで設定してください。

else { // コンテナが画面外にあるときの処理
 stopBubble();
};

else内には、コンテナが画面外に出たときの処理を指定します。

事前に用意していたstopBubble()を呼び出してあげることで、泡の生成をストップさせましょう。

これがないと、コンテナが画面外にあるときも泡が生成され続け、無駄なspan要素が増えてしまいます。

次にIntersection observerに渡すオプションを設定します。

// Intersection observerに渡すオプション
const options = {
  rootMargin: "100px 0px"
}

他にも渡せるオプションはあるのですが、ここで設定するのはrootMarginのみ。

rootMarginの値に100px 0pxを指定していますが、これはコンテナが画面内に入る100px手前で泡の生成を開始するという意味になります。

泡の生成を少し手前で開始しておくことで、コンテナが画面内に入った段階ですでに泡がいくつか表示されている状態になります。

最後に、Intersection observerを初期化して完成です!

// Intersection observerの初期化
const io = new IntersectionObserver(cb, options);
io.POLL_INTERVAL = 100; // Polyfill
io.observe(section);

お疲れさまでした。これで泡のアニメーションが実装できるはずです。

【まとめ】泡のアニメーションはJSとCSSで簡単に作れる!

以上、ふわふわ漂う泡のアニメーションの実装方法を紹介しました。

コピペで使えるようにしてあるので、ぜひWebサイトやポートフォリオに取り入れてみてください。

カスタマイズについて質問等ありましたら、コメント欄からどうぞ。

記事が役に立ったらサポートしてください

Web制作に関する
記事案を募集中!

Web制作について知りたいこと、質問等ありましたら、以下のフォームから気軽に投稿してください。
要望が多かったものは解説記事を作成します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメント一覧 (6件)

  • はじめまして、藤木と申します。
    Webサイト制作を行っており、今行っている案件でバブルを表示させる必要があり、
    当サイトをご参考にさせていただきました。
    1点教えていただきたいことがありまして、今は永続的に泡が発生していますが、泡の発生を断続的に行うことはできないでしょうか?
    具体的には、2秒間隔で泡を発生させたいと考えています。
    setintervalで実装できるかと思い、色々試しましたが、動いてくれません。

    何卒ご教授のほどよろしくお願いいたします。
    藤木

    • はじめまして。記事を読んでくださりありがとうございます!
      以下のコードを一度試していただけますか?


      document.addEventListener("DOMContentLoaded", () => {
      // コンテナを指定
      const section = document.querySelector(".bubble-background");

      // 泡を生成する関数
      const createBubble = () => {
      const bubbleEl = document.createElement("span");
      bubbleEl.className = "bubble";
      const minSize = 10;
      const maxSize = 50;
      const size = Math.random() * (maxSize + 1 - minSize) + minSize;
      bubbleEl.style.width = `${size}px`;
      bubbleEl.style.height = `${size}px`;
      bubbleEl.style.left = Math.random() * innerWidth + "px";
      section.appendChild(bubbleEl);

      // 一定時間が経てば泡を消す
      setTimeout(() => {
      bubbleEl.remove();
      }, 8000);
      };

      // 泡の生成を開始するトリガー(初期値はOFF)
      let activeBubble = null;

      // 2秒間泡が発生した後、泡をストップ
      function bubbleTimer() {
      activeBubble = setInterval(createBubble, 100);
      setTimeout(() => {
      clearInterval(activeBubble);
      }, 2000);
      }

      // 4秒ごとに↑を繰り返す
      setInterval(() => {
      bubbleTimer();
      }, 4000);

      // 最初の1回を実行
      bubbleTimer();
      });

      泡を2秒間生成→ストップを4秒ごとに繰り返しています。(Intersection Observerは複雑になるため省いています)
      よろしくお願いします!

  • 御回答ありがとうございます。
    実行したい挙動が実現でき、非常に助かりました。

    1点確認させてください。
    画面に入ったときに泡を発生させ、画面から出たときに泡を停止させる「Intersection Observer」ですが、画面に入る前から泡を表示させても良い場合は、削除しても良いですよね?
    泡自体は、何秒後かに消えるようにされているため、問題ないように思うのですが、いかがでしょうか?

    質問の意図は、「Intersection Observer」があることで永続的に泡が発生して、目的の挙動をしなかったためです。

    以上、よろしくお願いいたします。

    • はい、その通りですね!
      画面に入ってから泡が発生→画面外に出るとストップという挙動が不要な場合は、削除いただいて問題ありません。

  • ありがとうございます!
    非常に助かりました。
    これからもよろしくお願いします!

Tatsuya へ返信する コメントをキャンセル

目次