Firefoxでkeyframeアニメーションがカクカクしてしまう時の対処法

この記事は最終更新から4年以上が経過しています。情報が古くなっている可能性があります。

こんにちは、しばです。
この間Animate.css & wow.jsを使っていたところ、とあるアニメーションがFirefoxで非常にカクカクとした挙動になることが発覚。
Firefoxはシェア率が低いためいつもスルーしてるんですが(2018/7時点だと6.43%らしい)、今回仕事でFirefoxの表示確認が必要だったので調べてみました。

結論から言えばwow.jsは関係なく、@keyframeの解釈が他のブラウザとFirefoxではちょっと異なることが原因でした。詳細をまとめておきたいと思います。

Firefoxでのカクカクとした挙動

今回は、Animate.cssでflipというアニメーションを使った時に発生しました。
flipは、本来だと以下のような挙動になります。


※これはgif画像

ところが、Firefoxだとこうなる。

半分回転したところで急に拡大、縮小してまた半分回転という動きになってしまっています。(Windows、Macどちらとも)
これは公式のデモの時点でこうなっていました。

調査

flipのAnimate.cssのコードはこちら。

@keyframes flip {
  0% {
    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
    transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out
  }
  40% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
    transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out
  }
  50% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
    transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in
  }
  80% {
    -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95);
    transform: perspective(400px) scale3d(0.95, 0.95, 0.95);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in
  }
  100% {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in
  }
}
/* @-webkit-keyframes省略 */

※最新版のコードとちょっと違うかもですが対応方法は多分同じ

まず、css3アニメーションがおかしくなる原因を調べてみました。

  • Firefoxではanimation-delayに0を指定してると動かない→0sと書こう!
  • CSS3アニメーション属性の接頭語に「-moz-」がない→「-moz-」を追加しよう!
  • CPUだけで処理してるから遅くなってカクカクする→GPU処理にするためにtranslate3d使おう!

だいたいこんな理由が多いようです。
が、今回はあてはまらず。

アニメーションを見る限り、問題あるのは回転中に奥行き(Z軸)の移動がある箇所なので、奥行きに関係ある「perspective」と「translated3d」が何かしら関わってるのではと予想。
実際にprespectiveを全てコメントアウトすると、カクカクとした動きはなくなりました。

しかしperspectiveの値を調整しても一向に直る気配がない。。
親要素に適用できるperspectiveプロパティを使用すると滑らかになるけど軸が変わってしまう。。

などと苦戦していたのですが、こちらの記事をヒントにさらっと解決できました。

Firefoxはkeyframesで記述している着地地点に最短の動作をするため、他の地点でのプロパティの記述を省略すると、chromeでは滑らかに着地地点に向かって変化するのに対し、Firefoxは直線的に変化してしまうようです。
(こちらの記事ではsafariもカクカクしたようですが、今回はsafariは問題なくFirefoxでのみ事象を確認)

そのため、途中過程もしっかり記述を補強してあげることで無事直りました!
flipの記述の例だと以下でスムーズに動くようになりました。

@keyframes flip {
  0% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg);
    transform: perspective(400px) translate3d(0, 0, 0) rotate3d(0, 1, 0, -360deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }
  40% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
    transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
    -webkit-animation-timing-function: ease-out;
    animation-timing-function: ease-out;
  }
  50% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 140px) rotate3d(0, 1, 0, -170deg);
    transform: perspective(400px) translate3d(0, 0, 140px) rotate3d(0, 1, 0, -170deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
  60% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 120px) rotate3d(0, 1, 0, -150deg);
    transform: perspective(400px) translate3d(0, 0, 120px) rotate3d(0, 1, 0, -150deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
  70% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 100px) rotate3d(0, 1, 0, -100deg);
    transform: perspective(400px) translate3d(0, 0, 100px) rotate3d(0, 1, 0, -100deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
  80% {
    -webkit-transform: perspective(400px) translate3d(0, 0, 0) scale3d(0.95, 0.95, 0.95);
    transform: perspective(400px) translate3d(0, 0, 0) scale3d(0.95, 0.95, 0.95);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
  100% {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
}
/* @-webkit-keyframes省略 */

※ゴリ押しなので、もしかしたらもっとスマートな記述で済むかもしれない

まとめ

この事象が起きない場合も多いかと思いますが、Firefoxでcssアニメーションがカクカクした動きになってしまう場合は、@keyframeの中継地点に気をつけよう!という話でした。

余談ですが、今回調べる中で結構いろんなサイトでFirefoxがカクカクしてました。やっぱり対応ブラウザから外してるところ多いのかな🤔