LAB
EvoLab メインビジュアル(2023.10.25〜)
INDEX
コンセプト
10月25日はEVOWORXの創立記念日。
たくさんの方々に支えられ、無事16周年を迎えることができました。今回はそんなEVOWORXの「創立記念日」とEVOのエンジニアのモチーフである「ねこ」を掛け合わせ、
「扉」から猫が飛び出す、新しい始まりを表現したアニメーションを作成しました。企画からイラスト制作、アニメーション実装の細部までこだわり、これからのEVOWORXへ想いを込めました。
説明
gsapでタイムラインを制御したCSSアニメーションで実装しました。
使用したライブラリ
GSAP
Github:https://github.com/greensock/GSAP
公式サイト:https://greensock.com/gsap/
サンプル
コードの詳しい中身はこちらからご確認ください
ポイント
CSS
アニメーションのトリガーはクラスの追加によって行っています。
扉の画像はクラスの追加に合わせてwidth
とheight
を変化させています。
.door {
position: absolute;
top: 75%;
left: 50%;
width: 300vw;
height: calc(324 / 466 * 300vw);
transform: translateX(-50%) translateY(-50%);
transition: 1.6s cubic-bezier(0.55, 0.055, 0.675, 0.19);
background-color: #46E678;
-webkit-mask-image: url("https://evoworx.dev/_nuxt/img/mv_bg.11e83bf.png");
-webkit-mask-size: 100% 100%;
-webkit-mask-repeat: no-repeat;
-webkit-mask-position: center center;
mask-image: url("https://evoworx.dev/_nuxt/img/mv_bg.11e83bf.png");
mask-size: 100% 100%;
mask-repeat: no-repeat;
-webkit-mask-position: center center;
transform-origin: bottom center;
@media screen and (max-width: 767px) {
height: 250vh;
width: calc(466 / 324 * 250vh);
top: 50%;
transition: 1.6s cubic-bezier(0.55, 0.055, 0.2, 0.19);
}
&.is-scale {
width: 466px;
height: 324px;
top: 50%;
@media screen and (max-width: 767px) {
width: calc((254 / 375) * 100vw);
height: calc((176 / 375) * 100vw);
}
}
&.is-wait {
width: calc(466px * 0.4);
height: calc(324px * 0.4);
top: 50%;
transition: 0.6s cubic-bezier(.12,.38,.95,.62);
@media screen and (max-width: 767px) {
width: calc(((254 / 375) * 100vw) * 0.4);
height: calc(((176 / 375) * 100vw) * 0.4);
}
}
&.is-over {
width: 466px;
height: 324px;
top: 50%;
transition: 0.6s cubic-bezier(0.25, 1, 0.5, 1);
@media screen and (max-width: 767px) {
width: calc((254 / 375) * 100vw);
height: calc((176 / 375) * 100vw);
}
}
}
図形や風船が画面中央に吸い込まれていく動きは、transform
プロパティのscale
を調整して実装します。
.img-scale {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
.img {
position: absolute;
top: 50%;
left: 50%;
width: 100vw;
height: calc((1664 / 2880) * 100vw);
@media screen and (max-width: 767px) {
height: 100vh;
width: calc((792 / 458) * 100vh);
}
img {
width: 100%;
height: 100%;
}
}
.img-item-1 {
transition: 2.4s cubic-bezier(0.55, 0.055, 0.675, 0.19);
transform: translateX(-50%) translateY(-50%) scale(13);
@media screen and (max-width: 767px) {
transform: translateX(-50%) translateY(-50%) scale(10.5);
}
}
.img-item-2 {
transition: 2.4s cubic-bezier(0.55, 0.055, 0.675, 0.19) 0.4s;
transform: translateX(-50%) translateY(-50%) scale(13);
@media screen and (max-width: 767px) {
transform: translateX(-50%) translateY(-50%) scale(10.5);
}
}
&.scale-down {
.img {
transform: translateX(-50%) translateY(-50%) scale(0);
}
}
}
猫たちが飛び出すアニメーションは、画像の初期位置を画面の中央にし、.is-over
クラスの追加後にtransform
プロパティのtranslate
とscale
を調整して実装しています。
.block-out {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
visibility: hidden;
.item {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%) scale(0.3);
transition: 1s cubic-bezier(0.25, 1, 0.5, 1);
transform-origin: center center;
img {
width: 100%;
height: 100%;
}
}
/* 省略 */
.item-cat-leader {
width: 150px;
height: 303px;
transform: translateX(-50%) translateY(-50%) scale(0.5) rotate(45deg);
@media screen and (max-width: 767px) {
width: calc((82 / 375) * 100vw);
height: calc((166 / 375) * 100vw);
}
}
/* 省略 */
&.is-over {
opacity: 1;
visibility: visible;
.item-cat-leader {
transform: translateX(-134px) translateY(-113.5px) scale(1) rotate(0deg);
@media screen and (max-width: 767px) {
transform: translateX(calc(-1 * ((74 / 375) * 100vw))) translateY(calc(-1 * ((62 / 375) * 100vw))) scale(1) rotate(0deg);
}
}
}
}
JS
GSAPのTimelineを使って、アニメーションのトリガーとなるクラスを付与するタイミングを設定しています。
Timelineのadd
メソッドではコールバック関数を追加することができるので、classList.add
を使用してクラスを付与しています。
各アニメーションの発火タイミングはadd
メソッドの引数position
で指定し、直前のアニメーションが終了した何秒後に発火するかを記述しています。
直前のアニメーションのtransition-duration
と合わせることで連続して動くように見せています。
const openTL = gsap.timeline({'paused': true});
const scale = document.querySelector('.img-scale');
const door = document.querySelector('.door');
const block = document.querySelector('.block-out');
const section = document.querySelector('.artwork');
const link = document.querySelector('.item-cat-koizumi');
const blink = document.querySelector('.blink-icon');
openTL
.add(() => { scale.classList.add('scale-down'); }, '+=0.8')
.add(() => { door.classList.add('is-scale'); }, '+=1.7')
.add(() => { door.classList.add('is-wait'); }, '+=1.6')
.add(() => { door.classList.remove('is-scale'); }, '<')
.add(() => { door.classList.add('is-over'); }, '+=0.6')
.add(() => { door.classList.remove('is-wait'); }, '<')
.add(() => { block.classList.add('is-over'); }, '+=0.1')
.add(() => { section.classList.add('is-show'); }, '<')
.add(() => { door.classList.add('flag-show'); }, '<')
.add(() => { link.classList.add('is-scale'); }, '+=1.15')
.add(() => { blink.classList.add('is-blink'); }, '<');
document.addEventListener('DOMContentLoaded', function(){
openTL.play();
});