CSS discreteアニメーションで画像切り替えスライドショーを作ってみた【JS不要】

2026.04.17 09:00
2026.03.16 13:33
CSS discreteアニメーションで画像切り替えスライドショーを作ってみた【JS不要】

CSSアニメーションって、普通は数値をなめらかに変化させるものですよね。opacityを0から1にフェードさせたり、transformで要素を移動させたり。でも「なめらかに変化させる必要がないプロパティ」もあるんですよね。

たとえばdisplayvisibilitybackground-imageなど。これらは中間値が存在しないので、従来はCSSアニメーションで扱えませんでした。

そこで登場したのがdiscreteアニメーションです。今回はこのdiscreteアニメーションを使って、JavaScriptなしで画像スライドショーやテキスト切り替えを実装してみました。

サンプルページを見る(スライドショー・テキスト切り替え・フェードインのインタラクティブデモ)

discreteアニメーションとは

CSSアニメーションの補間方法には大きく2種類あります。

  • 連続的(continuous): 数値を滑らかに変化させる(opacity, transform, colorなど)
  • 離散的(discrete): 値をパッと切り替える(display, visibility, background-imageなど)

discreteアニメーションは、この「離散的」な変化をCSSアニメーションで制御できる仕組みです。中間値を計算するのではなく、指定したタイミングで値をバッと切り替えます。

イメージとしては、スイッチのON/OFFみたいな感じですね。フェードではなく、パッと切り替わる動きになります。

steps()タイミング関数の使い方

discreteアニメーションで重要になるのがsteps()タイミング関数です。easelinearの代わりに使います。

/* 基本構文 */
animation-timing-function: steps(ステップ数, jump-term);

/* 例: 4ステップで切り替え */
animation-timing-function: steps(4, jump-none);

第2引数のjump-termには以下の値を指定できます。

  • jump-start(= start): アニメーション開始時にすぐ次のステップへジャンプ
  • jump-end(= end): 各区間の終わりで次のステップへジャンプ(デフォルト)
  • jump-none: 最初のフレーム(0%)と最後のフレーム(100%)の値をどちらも表示する
  • jump-both: 最初と最後に追加のステップが入る

スライドショーのように均等に切り替えたい場合はjump-noneが便利です。たとえば画像が4枚ならsteps(3, jump-none)と指定します。ステップ数は「切り替え回数」なので、画像枚数 – 1になるわけですね。

transition-behavior: allow-discreteとの関係

CSS Transitionsでdiscreteなプロパティを扱いたい場合は、transition-behavior: allow-discreteを指定する必要があります。

.element {
  transition: opacity 0.3s, display 0.3s;
  transition-behavior: allow-discrete;
}

/* ショートハンドでも書ける */
.element {
  transition: opacity 0.3s, display 0.3s allow-discrete;
}

これを指定しないと、displayなどの離散的プロパティはtransitionの対象外になります。一方、@keyframesを使ったアニメーションの場合はこの指定は不要です。@keyframes内ではdiscreteなプロパティを直接切り替えられます。

整理するとこうなりますね。

  • transitionでdiscreteプロパティを扱う → transition-behavior: allow-discreteが必要
  • @keyframesでdiscreteプロパティを扱う → 特別な指定は不要

実例1: 画像スライドショー(JS不要)

それでは実際にdiscreteアニメーションを使って、JavaScriptなしのスライドショーを作ってみました。background-image@keyframesで切り替えます。

<div class="slideshow"></div>
.slideshow {
  width: 600px;
  height: 400px;
  background-size: cover;
  background-position: center;
  animation: slide 12s steps(3, jump-none) infinite;
}

@keyframes slide {
  0% {
    background-image: url("img1.jpg");
  }
  33.33% {
    background-image: url("img2.jpg");
  }
  66.66% {
    background-image: url("img3.jpg");
  }
  100% {
    background-image: url("img4.jpg");
  }
}

ポイントはsteps(3, jump-none)の部分です。4枚の画像を切り替えるので、ステップ数は3(画像枚数 – 1)にしています。jump-noneを使うことで、0%の値(img1.jpg)と100%の値(img4.jpg)がどちらもきちんと表示されるんですよね。

12秒で4枚を切り替えるので、1枚あたり約3秒の表示時間です。infiniteでループさせれば、延々と回り続けるスライドショーの完成ですね。

実例2: テキスト切り替えアニメーション

次はcontentプロパティを使ったテキスト切り替えアニメーションを作ってみました。ヒーローセクションのキャッチコピーなんかに使えそうです。

<p class="rotating-text">
  私たちは<span class="dynamic-word"></span>を提供します
</p>
.dynamic-word::after {
  content: "デザイン";
  animation: swap-text 9s steps(2, jump-none) infinite;
  font-weight: bold;
  color: #e74c3c;
}

@keyframes swap-text {
  0% {
    content: "デザイン";
  }
  50% {
    content: "スピード";
  }
  100% {
    content: "安心";
  }
}

「デザイン」→「スピード」→「安心」と3つのテキストが順番に切り替わります。3つの値を切り替えるのでsteps(2, jump-none)で、キーフレームは0%・50%・100%の3点です。contentは離散的なプロパティなので、steps()との相性がぴったりですね。

実例3: display切り替えでのフェードイン/アウト

最後に、display: noneからのフェードインを実装してみました。これはtransition-behavior: allow-discrete@starting-styleを組み合わせるパターンです。

<div class="card">
  <h3>カードタイトル</h3>
  <p class="card-detail">ホバーすると表示される詳細テキストです。</p>
</div>
.card-detail {
  display: none;
  opacity: 0;
  transition: opacity 0.5s, display 0.5s allow-discrete;
}

.card:hover .card-detail {
  display: block;
  opacity: 1;

  /* フェードイン開始時の状態を指定 */
  @starting-style {
    opacity: 0;
  }
}

ここで重要なのが@starting-styleです。display: noneからblockに切り替わるとき、要素はまだレンダリングされていないので、ブラウザはtransitionの開始値を取得できません。@starting-styleで「この値からtransitionを始めてね」と教えてあげる必要があるんですよね。

この方法なら、これまでJavaScriptでclassを付け外ししていた表示/非表示アニメーションが、CSSだけで完結します。コードがかなりスッキリしますね。

background-imageの切り替えについて

background-imageの切り替えは、実例1のように@keyframesで直接指定するのが基本です。ただし注意点がいくつかあります。

  • background-imageはdiscreteなプロパティなので、画像同士のクロスフェードはできない
  • 切り替え時にちらつく場合は、画像のプリロードが必要
  • transitionでbackground-imageを切り替えたい場合はtransition-behavior: allow-discreteが必要

画像のプリロードはlinkタグで対応できます。

<link rel="preload" as="image" href="img1.jpg">
<link rel="preload" as="image" href="img2.jpg">
<link rel="preload" as="image" href="img3.jpg">
<link rel="preload" as="image" href="img4.jpg">

プリロードしておけば、切り替え時に画像の読み込み待ちが発生しません。スムーズなスライドショーになりますね。

ブラウザ対応状況

discreteアニメーション関連のブラウザ対応状況をまとめました(2026年3月時点)。

  • @keyframesでのdiscreteプロパティ: Chrome 116+、Firefox 130+、Safari 17.4+
  • transition-behavior: allow-discrete: Chrome 117+、Firefox 129+、Safari 17.4+
  • @starting-style: Chrome 117+、Firefox 129+、Safari 17.5+

2025年以降のモダンブラウザではほぼ対応済みです。古いブラウザ向けには@supportsでフォールバックを用意しておくと安心ですね。

/* フォールバック例 */
@supports (transition-behavior: allow-discrete) {
  .card-detail {
    transition: opacity 0.5s, display 0.5s allow-discrete;
  }
}

まとめ

今回はCSSのdiscreteアニメーションを使って、JavaScriptなしでスライドショーやテキスト切り替え、display切り替えのフェードインを実装してみました。

ポイントをまとめるとこんな感じですね。

  • discreteアニメーションは離散的なプロパティ(display, visibility, background-imageなど)をアニメーションできる仕組み
  • steps()タイミング関数で段階的な切り替えを制御する
  • @keyframesならそのまま使えるが、transitionではtransition-behavior: allow-discreteが必要
  • @starting-styleと組み合わせればdisplay: noneからのフェードインも可能
  • モダンブラウザでは対応済みなので実用段階に入っている

JSに頼らずCSSだけでできることがどんどん増えてきていて面白いですね。特にdisplay切り替えのアニメーションは、これまで地味に面倒だったのでかなり助かります。

サンプルページで実際の動きを確認する

今回は以上です!