Slick の current-slide でキャプションをアニメーション表示させる際、初回ロード時にもアニメーションさせる

参考サイトのように Slick の current-slide を使ってキャプションをアニメーション表示させようとしました。しかし、初回ロード時はアニメーションが反映されず、突如表示されてしまいます。具体的には

  • 画面表示 (初回ロード時、リロード時) のスライド1枚目のキャプションのみアニメーションが発火しない
  • 2週目以降はスライド1枚目のキャプションもアニメーションが発火する

という状態です。そこで、初回ロード時にもアニメーションさせる方法を模索しました。

コード (現象確認)

まずは現象を確認するためのコードを。

html

<ul class="slider defaultSlider">
    <li class="slider_first">
        <p class="caption">1枚目</p>
        <div class="slider_imgWrapper">
            <img src="https://placehold.jp/24/ff9100/db0303/1200x800.jpg">
        </div>
    </li>
    <li class="slider_second">
        <p class="caption">2枚目</p>
        <div class="slider_imgWrapper">
            <img src="https://placehold.jp/24/ff9100/db0303/1200x800.jpg">
        </div>
    </li>
    <li class="slider_last">
        <p class="caption">3枚目</p>
        <div class="slider_imgWrapper">
            <img src="https://placehold.jp/24/ff9100/db0303/1200x800.jpg">
        </div>
    </li>
</ul>

例えばこんな Slick のカルーセルを用意します。

JavaScript

$(() => {
    //slick
    const $slider = $('.slider');
    $slider.slick({
        autoplay: true,
        autoplaySpeed: 5000
    });
});

css(Scss)

@charset "utf-8";

//slick
.slider {
    overflow: hidden;
    position: relative;
    height: calc(100vw / 1200px * 800px);
    max-height: 80vh;
    z-index: 1;
    li {
        img {
            width: 100%;
        }
    }
}
$transition-speed: .3s;
$transition-delay: .5s;
.defaultSlider .slick-slide .caption {
    opacity: 0;
    transition: opacity $transition-speed ease;
}
.defaultSlider .slick-current .caption {
    opacity: 1;
    transition-delay: $transition-delay;
}

これで動作を確認すると、初回ロード時やリロード時といった最初の表示のときのみ transitionアニメーション が発火せず、ただキャプションが表示されるだけになってしまいます。

そこで以下のように改修。具体的には、初回ロード時等の一度限りで @keyframesアニメーション を用いて transitionアニメーション を再現することにしました。

コード (改修後)

HTML と JavaScript はそのまま。

css(Scss)

@charset "utf-8";

//slick
.slider {
    overflow: hidden;
    position: relative;
    height: calc(100vw / 1200px * 800px);
    max-height: 80vh;
    z-index: 1;
    li {
        img {
            width: 100%;
        }
    }
}
$transition-speed: .3s;
$transition-delay: .5s;
/* 初回ロード時のみ Slick の transition アニメーションが効かないため、独自にcss @keyframes で同じ transition アニメーションを指定 */
@keyframes firstLoadFade {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}
.ingenuitySlider .slider_first .caption {
    opacity: 0;
    animation: firstLoadFade $transition-speed ease $transition-delay 1 normal none running;
}
.ingenuitySlider .slick-slide .caption {
    opacity: 0;
    transition: opacity $transition-speed ease, visibility $transition-speed ease;
}
.ingenuitySlider .slick-current .caption {
    opacity: 1;
    transition-delay: $transition-delay;
}

肝は2点。

  • @keyframesアニメーション のプロパティ:
    • 1回限りなので 1 normal none running 指定
  • transitionアニメーション で指定するプロパティ:
    • アニメーション対象を最小限にするために opacity, visibility を指定

余談

改修に至るまでいくつか試行錯誤した結果をついでに書き記しておきます。

  • JavaScript側で処理:
    • Slick のイベントの init を使おうと思ったのですが、上手く反応しなかったので没
      • 最初 initイベント が発火している様子がなく、理由を探ったらイベントへのバインド ($slider.on('init', function () { /* 処理 */ });) は $slider.slick({ /* 処理 */ }); より前に行っていないとダメなようでした
      • その後、「初回だけ」の処理のために init で色々処理をして、それが次のスライドに切り替わる(少なくともカルーセルが1週するまでの間)にはフックやスタイルを外さなければならないため、チェーンメソッドが長くなりそうな雰囲気がしたため没に
        • afterChange は「前のスライド」を引数で持ってくることはできなさそうだった、というのも没になった点
  • css側で処理:
    • slick-initializedクラス でのプロパティ指定は特に効果なし
    • transitionアニメーション で指定するプロパティについて、最初は all を指定していましたが、 font-size 等関係ない部分もアニメーションが効いてしまうため、最小限の指定となるように opacity のみを指定
    • しかし、 opacity のみでは @keyframesアニメーション が動作せず、最初の状況と同じ状態になってしまいました。調査した結果、 visibility も追加することで意図した挙動になりました

参考

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。