🤖

animationendとtransitionendを使って複数要素にちょっと複雑な動きをさせる

2021/04/08に公開

概要

  • なんかちょっと複雑なアニメーションを頼まれた時に、楽していい感じにかけないかなぁと思った
  • @keyframesの%指定だけだとタイミングとかが微妙にずれることもあるし、ちょっとだるいなぁ
  • せや、animationendtransitionend使って別々の関数発火させればコントロール可能なんちゃうか??

動作確認

http://runstant.com/supermuscles/projects/aa202319

code

index.html
index.html
<div class="s500 bg-lightgray f fh">
      <div id='container' class="relative s200 bg-black f fh">
        <div class=' text-white fs24 bold'>
          END
        </div>        
        <div id='item1' class="absolute t0 l0 bg-red s-full f fh text-white fs24 bold overflow-hidden transition-1024">
          item 1  
        </div>
        <div id='item2' class="absolute t0 l0 bg-blue s-full f fh text-white fs24 bold overflow-hidden transition-1024">
          item 2
        </div>
        <div id='item3' class="absolute t0 l0 bg-green s-full f fh text-white fs24 bold overflow-hidden transition-1024">
          item 3
        </div>
      </div>
    </div>
main.css
main.css
.animation-1 {
  animation-name: scale;
  animation-duration: 3s;
  animation-timing-function: ease-out;
  animation-fill-mode: forwards;
}

@keyframes scale {
  from {
    transform: scale(1.0);
  }
  to {
    transform: scale(1.5);
  }
}
main.js
main.js
window.onload = function() {
  var $item1 = document.getElementById('item1');
  var $item2 = document.getElementById('item2');
  var $item3 = document.getElementById('item3');
  var items = [$item1, $item2, $item3];
  setDefault(items);
  setAnimationEnd(items);
  setTransitionEnd(items);
 };
 
// 初期クラスをセットする
var setDefault = (items) => {
  var arr = Array.isArray(items) ? items : Array.from(items);
  arr.forEach((item, index) => {
    switch (index) {
      case 0:
        item.classList.add('z3', 'animation-1');
        break;
      case 1:
        item.classList.add('z2');
        break;
      case 2:
        item.classList.add('z1');
        break;
    }
  });
};

// animation終了時のイベントに関数を登録する
var setAnimationEnd = (items) => {
  var arr = Array.isArray(items) ? items : Array.from(items);
  arr.forEach((item, index) => {
    item.onanimationend = onAnimationEnd;
  });
};

// アニメーション終了時にトランジションさせるためのクラス付け替え
var onAnimationEnd = (e) => {
  e.currentTarget.classList.remove('s-full');
  e.currentTarget.classList.add('s0');
  console.log('animation end');
};

// transition終了時のイベントに関数を登録する
var setTransitionEnd = (items) => {
  var arr = Array.isArray(items) ? items : Array.from(items);
  arr.forEach((item, index) => {
    item.ontransitionend = onTransitionEnd;
  });
  
};

// トランジション終了時に次の要素のアニメーションを始めるための関数を呼ぶ
var onTransitionEnd = (e) => {
  console.log('transition end');
  var next = e.currentTarget.nextElementSibling;
  if (!next) return ;
  setNextItem(next);
};

// アニメーションを開始するクラスをつける
var setNextItem = (item) => {
  item.classList.add('animation-1');
};

解説

流れ

  1. 一個目の要素が大きくなるアニメーションを始める
  2. 大きくなるアニメーションが終わり次第、大きさを小さくしていく
  3. 大きさが0になったら、次の要素のアニメーションを始める
  4. 上を要素がなくなるまで繰り返す

onanimationend

  • animationが終了した時に発火するイベント
  • @keyframesで登録したアニメーションの場合は、こちらが発火する
  • 今回で言うと要素が3秒間かけてscale(1.5)になるアニメーション
  • 1.5倍になったらonanimationendが発火する

ontransitionend

  • transitionが終了した時に発火するイベント
  • 今回で言うと.transition-1024というクラスをつけており、1024msかけて要素が変わるようにしている
  • widthとheightが100%の状態から、0pxにするというトランジション
  • 0pxになったらontransitionendが発火する

まとめ

  • 多分アニメーションのプロフェッショナルの方はこんなんやらなくてもanimation-timing-functionのcubic-bezierとかで完璧なタイミングのアニメーションが作れるかもしれないが、自分は力不足だったので、こんな感じになった
  • onAnimationEnd関数の中でまたいろいろやれば、次の要素が見えた時にアニメーション始めたり、文字出したり、いろいろできるけど、今回は省略
  • onTransitionEnd関数の中でもっといい感じにやれば上記アニメーションを無限に繰り返すこともできるが、今回は省略
  • そしていつもながら見た目やアニメーション名がクソ適当なのはご容赦ください。

Discussion