【高度なアニメーション】単語ごとにフェードイン

はじめに

どうも、NNCの石上です。
今回は、ちょっと高度なアニメーション演出を紹介したいと思います。
CSS・jQueryについてある程度の知識がある前提で解説を進めて行きます。

単語ごとにフェードイン

アニメーションの挙動を言葉のみで全て説明するのはとても難しいのですが、以下の動画をご覧いただくとイメージがつくと思います。

画面内に要素が入った段階で、色付きのラインが左→右にフェードインで現れて、最後には文字だけが残るといった具合です。また、フェードインは一単語ずつタイミングをズラして行われます。

実際のコード

HTML / CSS / Javascriptの記述を見ていきます。

<section class="l-section p-midori">
    <h2 class="p-midori__head js-inView">
        <span class="p-midori__fade">
            <span class="p-midori__word js-inView">Sample</span>
            <span class="p-midori__fill js-inView"></span>
        </span>
        <span class="p-midori__fade">
            <span class="p-midori__word js-inView">Animation</span>
            <span class="p-midori__fill js-inView"></span>
        </span>
        <span class="p-midori__fade">
            <span class="p-midori__word js-inView">Text</span>
            <span class="p-midori__fill js-inView"></span>
        </span>
        <br>
        <span class="p-midori__fade">
            <span class="p-midori__word js-inView">by</span>
            <span class="p-midori__fill js-inView"></span>
        </span>
        <span class="p-midori__fade">
            <span class="p-midori__word js-inView">NNC</span>
            <span class="p-midori__fill js-inView"></span>
        </span>
        <span class="p-midori__fade">
            <span class="p-midori__word js-inView">STUDIO</span>
            <span class="p-midori__fill js-inView"></span>
        </span>
    </h2>
</section>
.p-midori__head {
  font-size: 30px;
  font-weight: 900;
}
.p-midori__fade {
  position: relative;
  display: inline-block;
}
@keyframes passing-fill {
  0% {
    left: 0;
    width: 0;
  }
  50% {
    left: 0;
    width: 100%;
  }
  51% {
    left: 0;
    width: 100%;
  }
  100% {
    left: 100%;
    width: 0;
  }
}
@keyframes passing-word {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
.p-midori__word {
  position: relative;
  opacity: 0;
  transform: translate3d(0, 0, 0);
}
.p-midori__word.is-active {
  animation: passing-word 0.4s cubic-bezier(0.25, 1, 0.5, 1) forwards;
}
.p-midori__fill {
  position: absolute;
  display: block;
  width: 0;
  height: 100%;
  top: 0;
  left: 0;
  background-color: #333;
  transform: translate3d(0, 0, 0);
}
.p-midori__fill.is-active {
  animation: passing-fill 0.8s cubic-bezier(0.25, 1, 0.5, 1) forwards;
}
.p-midori .p-midori__fade:nth-of-type(1) .p-midori__word.is-active {
  animation-delay: calc( 0.2s + (0.1s * 1) );
}
.p-midori .p-midori__fade:nth-of-type(1) .p-midori__fill.is-active {
  animation-delay: calc( 0.1s * 1);
}
.p-midori .p-midori__fade:nth-of-type(2) .p-midori__word.is-active {
  animation-delay: calc( 0.2s + (0.1s * 2) );
}
.p-midori .p-midori__fade:nth-of-type(2) .p-midori__fill.is-active {
  animation-delay: calc( 0.1s * 2);
}
.p-midori .p-midori__fade:nth-of-type(3) .p-midori__word.is-active {
  animation-delay: calc( 0.2s + (0.1s * 3) );
}
.p-midori .p-midori__fade:nth-of-type(3) .p-midori__fill.is-active {
  animation-delay: calc( 0.1s * 3);
}
.p-midori .p-midori__fade:nth-of-type(4) .p-midori__word.is-active {
  animation-delay: calc( 0.2s + (0.1s * 4) );
}
.p-midori .p-midori__fade:nth-of-type(4) .p-midori__fill.is-active {
  animation-delay: calc( 0.1s * 4);
}
.p-midori .p-midori__fade:nth-of-type(5) .p-midori__word.is-active {
  animation-delay: calc( 0.2s + (0.1s * 5) );
}
.p-midori .p-midori__fade:nth-of-type(5) .p-midori__fill.is-active {
  animation-delay: calc( 0.1s * 5);
}
.p-midori .p-midori__fade:nth-of-type(6) .p-midori__word.is-active {
  animation-delay: calc( 0.2s + (0.1s * 6) );
}
.p-midori .p-midori__fade:nth-of-type(6) .p-midori__fill.is-active {
  animation-delay: calc( 0.1s * 6);
}
$(function(){
	$(window).scroll(function(){
		$('.js-inView').each(function(){
			var scroll = $(window).scrollTop();
			var pos = $(this).offset().top;
			var delay = 600;
			if (scroll > pos - delay){
				$(this).addClass('is-active');
			}
		});
	});
});

解説

大まかな流れは以下のようになります。

1. 単語ずつspanで括った上で、文字と色付きライン用のspanを別個で記述
2.CSSで、あらかじめis-activeが付与された時のためのアニメーションを用意しておく
3.画面内に要素が入ったタイミングでクラス(is-active)を付与する(アニメーションが始動)

まずはHTMLから見ていきましょう。
重要なのは、「単語ずつspan(.p-midori__fade)で区切ること」「文字を括る用のspan(.p-midori__word)と色付きライン用のspan(.p-midori__fill)をそれぞれ用意しておくこと」の2点でしょうか。あとは、要素が画面内に入ったかどうかを判定するための「.js-inView」というクラスもサンプルの様に付与しておく必要があります。

次にCSSです。まずはCSSアニメーションを用意しましょう。
色付きラインは「passing-fill」、テキストは「passing-word」という名前のアニメーションをそれぞれ用意しています。「passing-fill」では色付きライン(.p-midori__fill)のwidthとleftの値をイジることで、ラインが左から右へフェードするように見せています。「passing-word」は比較的単純で、ラインが過ぎ去るまでの間テキストを隠しておくためにopacityの値を制御するものになります。
「.is-active」というクラスが付与されたと同時にアニメーションを開始したいので、「.p-midori__fill.is-active」「.p-midori__word.is-active」をそれぞれセレクタとして、そこにanimationプロパティの記述を指定しておきます。動画で見たように単語ごとにタイミングをずらして見せたいので、「nth-of-type」と「animation-delay」を利用して時間差を実装していきます。
※CSSで書くとnth-of-typeをちまちま書いていく必要がありますが、SCSSで記述すると以下のように簡潔な記述で済みます。

@for $i from 1 through 6{
  .p-midori__fade:nth-of-type(#{$i}) {
     .p-midori__word.is-active {
          animation-delay: calc( 0.2s + (0.1s * #{$i}) );
      }
      .p-midori__fill.is-active {
          animation-delay: calc( 0.1s * #{$i});
      }
  }
}

最後にJavascriptです。
以前こちらの記事で紹介した内容を利用していますので、詳しい解説はそちらをご覧ください。
「js-inView」というクラスが付与されている要素を監視し、画面内に入った段階でそれに対して「is-active」というクラスを付与します。

以上で完成です!

RECENT ENTRIES

  • 【ウェブ解析】Core Web Vitalsを指標としたサイト改善方法

    VIEW MORE
  • 【jQuery】「jquery.cookie.js」で言語切り替え機能を実装する

    VIEW MORE
  • 【ウェブ解析】インプレッションにおけるSEOとMEOの重要性とは?

    VIEW MORE

CONTACT