CSS discreteアニメーションで画像切り替えスライドショーを作ってみた【JS不要】
CSSアニメーションって、普通は数値をなめらかに変化させるものですよね。opacityを0から1にフェードさせたり、transformで要素を移動させたり。でも「なめらかに変化させる必要がないプロパティ」もあるんですよね。
たとえばdisplayやvisibility、background-imageなど。これらは中間値が存在しないので、従来はCSSアニメーションで扱えませんでした。
そこで登場したのがdiscreteアニメーションです。今回はこのdiscreteアニメーションを使って、JavaScriptなしで画像スライドショーやテキスト切り替えを実装してみました。
▶ サンプルページを見る(スライドショー・テキスト切り替え・フェードインのインタラクティブデモ)
目次
discreteアニメーションとは
CSSアニメーションの補間方法には大きく2種類あります。
- 連続的(continuous): 数値を滑らかに変化させる(opacity, transform, colorなど)
- 離散的(discrete): 値をパッと切り替える(display, visibility, background-imageなど)
discreteアニメーションは、この「離散的」な変化をCSSアニメーションで制御できる仕組みです。中間値を計算するのではなく、指定したタイミングで値をバッと切り替えます。
イメージとしては、スイッチのON/OFFみたいな感じですね。フェードではなく、パッと切り替わる動きになります。
steps()タイミング関数の使い方
discreteアニメーションで重要になるのがsteps()タイミング関数です。easeやlinearの代わりに使います。
/* 基本構文 */
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切り替えのアニメーションは、これまで地味に面倒だったのでかなり助かります。
今回は以上です!