【Javascript】GSAPで高度なアニメーションをお手軽に実装しよう
前回の記事に引き続きGSAPのアニメーション実装について見ていきたいと思います。前回はごく基本的な機能についてでしたが、今回は新しい要素を交えて、より高度なアニメーションを実装していきたいと思います。
スクロールと連動させたり、時間差で連続性のあるアニメーションを試して見ましょう。下の動画が完成イメージの動きになります。
基本文法などはこちらの記事からも詳しくご覧いただけます!(別ページへ移動します)
導入準備
ライブラリを読み込みましょう。詳しい解説は前回の記事を参照してください。
<!-- gsap:ライブラリ本体とプラグイン -->
<script src="assets/js/libs/gsap.min.js"></script>
<script src="assets/js/libs/ScrollTrigger.min.js"></script>
サンプルコード
以下のコードをコピペすれば、すぐにアニメーションを確認できる筈です。HTMLとSCSSの説明はここでは割愛し、GSAPの記述について詳しく見ていきます。
<section class="l-section p-time">
<div class="p-time__img"></div>
<div class="p-time__text">
<h3 class="p-time__head">TITLE</h3>
<div class="p-time__body">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Vitae dolores, dolor, dicta quibusdam quod, nemo ducimus deleniti recusandae velit optio laborum natus impedit quisquam maxime libero inventore consectetur. Consequatur, deleniti!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Vitae dolores, dolor, dicta quibusdam quod, nemo ducimus deleniti recusandae velit optio laborum natus impedit quisquam maxime libero inventore consectetur. Consequatur, deleniti!</p>
</div>
</div>
</section>
<section class="p-side">
<div class="p-side__item p-side__item-first">ANIMATION</div>
<div class="p-side__item p-side__item-second">ANIMATION</div>
</section>
<section class="p-pin">
<div class="p-pin__box"></div>
</section>
<section class="l-section p-panel">
<div class="p-panel__list">
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
<li class="p-panel__item"></li>
</div>
</section>
.l-main {
padding: 200px 0;
}
.l-section {
width: 900px;
margin-left: auto;
margin-right: auto;
margin-top: 200px;
}
.l-header {
display: grid;
place-items: center;
width: 100vw;
height: 100vh;
color: #fff;
background-color: #222;
font-size: 2rem;
text-align: center;
}
// project
.p-time {
display: flex;
align-items: center;
justify-content: space-between;
&__img {
width: 320px;
aspect-ratio: 1 / 1.414;
background-color: #ccc;
}
&__text {
width: 480px;
}
&__head {
display: inline-block;
font-size: 2.4rem;
font-weight: 900;
}
&__body {
margin-top: 24px;
p {
margin-top: 12px;
}
}
}
.p-side {
margin-top: 200px;
&__item {
font-size: 16vw;
font-weight: 900;
line-height: 1;
}
}
.p-pin {
display: grid;
place-items: center;
width: 100vw;
height: 100vh;
background-color: #222;
&__box {
width: 400px;
aspect-ratio: 1/ 1;
background-color: #fff;
}
}
.p-panel {
width: 500px;
&__list {
display: grid;
grid-template-columns: repeat(auto-fit,50px);
justify-content: center;
gap: 6px;
list-style: none;
}
&__item {
width: 50px;
aspect-ratio: 1/1;
background-color: #222;
}
}
// スクロールトリガーの導入
gsap.registerPlugin(ScrollTrigger);
// time
sec = ".p-time";
img = ".p-time__img";
text = ".p-time__text";
head = ".p-time__head";
body = ".p-time__body";
para = ".p-time__body p";
// 初期状態
gsap.set(
[img,head],
{
autoAlpha: 0,
rotate: 4
}
);
gsap.set(img,{
scale: 0
})
gsap.set(para,{
autoAlpha: 0,
x: -12,
})
// タイムラインの作成・設定
tl = gsap.timeline({
defaults: {
ease: Expo.easeOut,
duration: 0.64
},
scrollTrigger: {
trigger: sec,
start: "top center",
end: "bottom top",
},
})
// タイムラインアニメーション
tl.to(img,{
autoAlpha: 1,
rotate: 0,
scale: 1
})
.to(head,{
autoAlpha: 1,
rotate: 0
},"+=0.04")
.to(para,{
duration: 0.4,
autoAlpha: 1,
x: 0,
stagger: 0.06
},"+=0.12")
// side
// 要素
side = ".p-side";
item = ".p-side__item";
first = ".p-side__item-first";
second = ".p-side__item-second";
// 初期状態
gsap.set(first,{
x: -1420
});
gsap.set(second,{
x: 1420
});
// アニメーション
gsap.to(item,{
x: 0,
scrollTrigger: {
trigger: side,
start: 'top center',
end: 'bottom center',
scrub: true
}
});
// pin
pin = ".p-pin";
box = ".p-pin__box"
gsap.to(box, {
borderRadius: 200,
backgroundColor: "#00a497",
scrollTrigger: {
pin: true, //宣言
trigger: pin, //固定したい要素
start: 'center center', //固定の開始位置
end: '+=1000', //固定する距離、ここでは1000px
scrub: true
}
});
// panel
panel = ".p-panel"
panelItem = ".p-panel__item";
// 初期状態
gsap.set(panelItem,{
autoAlpha: 0,
scale: 0
});
// タイムラインの作成・設定
tl = gsap.timeline({
defaults: {
ease: Expo.easeOut,
duration: 0.8
},
scrollTrigger: {
trigger: panel,
start: "top center",
end: "bottom top",
},
})
// アニメーション
tl.to(panelItem,{
autoAlpha: 1,
scale: 1,
stagger: {
each: 0.08,
from: "center",
grid: "auto"
}
});
Javascriptの解説
例の如く、冒頭でプラグイン使用の宣言をしておきます。
// スクロールトリガーの導入
gsap.registerPlugin(ScrollTrigger);
さて、今回の目玉の一つは「timeline」機能です。GSAPの基本機能でありながら最大の特徴と言っていいでしょう。連続したアニメーションを直感的に記述することができます。
// 要素
sec = ".p-time";
img = ".p-time__img";
text = ".p-time__text";
head = ".p-time__head";
para = ".p-time__body p";
// タイムラインの作成・設定
tl = gsap.timeline({
defaults: {
ease: Expo.easeOut,
duration: 0.64
},
scrollTrigger: {
trigger: sec,
start: "top center",
end: "bottom top",
},
})
// アニメーション
tl.to(img,{
autoAlpha: 1,
rotate: 0,
scale: 1
})
.to(head,{
autoAlpha: 1,
rotate: 0
},"+=0.04")
.to(para,{
duration: 0.4,
autoAlpha: 1,
x: 0,
stagger: 0.06
},"+=0.12")
まずは「tl = gsap.timeline({…})」でタイムラインを生成しておきます。easeやduration、スクロールイベントの検知位置などを設定しましょう。その後、「tl.to(要素,{アニメーションの内容})」をサンプルのように繋げて書くことで、アニメーションを連続して始動させることができます。ここで言うと、「img→head→para」の順番ですね。
さらに、アニメーションに関する記述の後に「”+=数字”」を記載しておくと、秒差も設定することができます。例えば、「”+=0.6″」だったら0.6秒後になりますね。
続けて、「scrub」についてです。アニメーションの進行度がスクロールの量と連動しているものを見たことがあると思いますが、それをお手軽に実装することができます。
// side
// 要素
side = ".p-side";
item = ".p-side__item";
first = ".p-side__item-first";
second = ".p-side__item-second";
// 初期状態
gsap.set(first,{
x: -1420
});
gsap.set(second,{
x: 1420続けて
});
// アニメーション
gsap.to(item,{
x: 0,
scrollTrigger: {
trigger: side,
start: 'top center',
end: 'bottom center',
scrub: true
}
});
こちらもとても簡単で、「scrollTrigger」の中で「scrub: true」と書くだけ。また、値に数字を入れるとアニメーションの秒数を設定することもできます。例えば、「scrub: 0.8」だったら0.8秒かかる、といった具合です。
次は「pin」についてです。ごく簡単に言うと「position: sticky」と同じような挙動をしてくれます。
// pin
pin = ".p-pin";
box = ".p-pin__box"
gsap.to(box, {
borderRadius: 200,
backgroundColor: "#00a497",
scrollTrigger: {
pin: true, //宣言
trigger: pin, //固定したい要素
start: 'center center', //固定の開始位置
end: '+=1000', //固定する距離、ここでは1000px
scrub: true
}
});
こちらも「scrollTrigger」の中で宣言をします。固定する距離もここで設定できるので、HTML/CSSでstickyを駆使するよりも直感的でお手軽でしょう。scrubと併用することで、完成イメージのようにインタラクティブで面白いアニメーションを実装できます。
最後は「stagger」についてです。これは前回の記事でも触れているので、全く新しい機能の紹介と言うわけではありませんが、これを使いこなすことで面白い表現ができるようになります。
// panel
panel = ".p-panel"
panelItem = ".p-panel__item";
// アニメーション
tl.to(panelItem,{
autoAlpha: 1,
scale: 1,
stagger: {
each: 0.08,
from: "center",
grid: "auto"
}
});
staggerを用いることで、同じクラス名の要素を時間差で動かすことができるようになりますが、「grid:”auto”」を記述することで、それらを格子状に制御することができます。サンプルでいうと、大量に並んだ正方形が左上から順に現れるのではなく、要素の配置座標を考慮した上でど真ん中から順にフェードインしてくる、といった具合です。「from」の値は「center」だけでなく、「random」「left」でも面白い表現になります ので、ぜひ試してみてください。
終わりに
前回に引き続き、GSAPを用いたアニメーション実装を行いました。やはり、timeline機能を利用してアニメーションを繋げられることが、このライブラリの最大の恩恵かなと思います。
個人的にはscrubも気に入っています。一から実装するとなると、かなり手間のかかりそうな演出なので…。
GSAPはこういった基本機能の他にも、scrollTriggerをはじめとした便利なプラグインも多数用意されています。これらを駆使して、高度なアニメーションをお手軽に実装してみましょう。