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

アニメーションのトリガーはクラスの追加によって行っています。

扉の画像はクラスの追加に合わせてwidthheightを変化させています。

.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プロパティのtranslatescaleを調整して実装しています。

.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();
});