◆ steps は keyframes の間を指定回数の段階でアニメーションする
◆ なめらかなものじゃなくて一瞬で切り替わる
◆ 0% と 100% を指定して 2 段階とすると
  ◆ 1 段目で半分なので 2 keyframes の中間状態
  ◆ 2 段目で最後の 100% に指定した keyframe の状態
◆ 指定 keyframes の 2 つを切り替えるなら steps は 1 つで 50% と 100% に keyframes を設定する

CSS Animation で滑らかなアニメーションではなくて 指定した状態間を一瞬で切り替えるのを繰り返したいです
例えば opacity が 0 と 1 を切り替えてフェードではなく点滅させるようなものです

同じ % に複数指定

グラデーションみたいに 50% に両方を指定すれば 50% のところで切り替わるかなと思いました
@keyframes anim {
0%, 50% { font-size: 10px; color: red; }
50%, 100% { font-size: 30px; color: blue; }
}

でも効果なしです
滑らかに animation します
0% から 50% までアニメーションして 50% から 100% は変化なしです
同じパーセントはあとのもので上書きされています

steps

次に見つけたのが timing-function の steps です

<!doctype html>

<style>
@keyframes anim {
from { font-size: 10px; color: red; }
to { font-size: 30px; color: blue; }
}
.anim {
animation: anim 3s steps(2, start) 0s infinite;
}
</style>

<div class="anim">aaaaaaaaaa</div>

linear とか ease を指定するところに steps 関数を設定できます
step というだけあってアニメーションの曲線が階段状になるようです
ease だとなめらかなものです

css-anim-tf

使ってみると 2 つの状態が交互に切り替わったのですが初期状態が from の値とは違う値のようです
getComputedStyle で各状態の実際の値を表示してみました
function showCurrentStyle(){
const style = getComputedStyle(document.querySelector(".anim"))
console.log(style.fontSize, style.color)
}
20px rgb(128, 0, 128)

30px rgb(0, 0, 255)

繰り返し

初期状態が from ではなく from と to のちょうど間の値です
2 つめの状態は to と同じものです


「steps(2, end)」にしてみると
10px rgb(255, 0, 0)

20px rgb(128, 0, 128)

繰り返し

初期値は from のものですが 2 つめの状態が from と to の中間です


「steps(3, start)」 にしてみると
16.6667px rgb(170, 0, 85)

23.3333px rgb(85, 0, 170)

30px rgb(0, 0, 255)

繰り返し

from と to の間に 2 段階の状態があって to の値になります
33 %, 66%, 100% の 3 段階のアニメーションといえます


あとは

steps(1, start)
⇨ to の値で変化なし

steps(1, end)
⇨ from の値で変化なし

steps(0, start)
⇨ keyframe の影響なく変化なし

でした

from と to の 2 つを交互に切り替える用途ではなくて アニメーションを始める前が from と同じスタイルで アニメーションで to に段階的に変化させるときに使うものみたいです

steps と keyframes

ここまでは from と to パーセントだと 0% と 100% しか指定していませんでした
私はてっきり keyframes からなめらかなアニメーション (linear) として各状態を計算した上で steps が 2 つなら 50% と 100% のところのスタイルで表示され 4 つなら 25%, 50%, 75%, 100% のところのスタイルで表示されると思っていました

ですが 実際には各 keyframes 間の変化が steps で指定した回数に分けて切り替わります
keyframes が 2 つ 0% と 100% なら変化は 1 つなのでそこを 2 段階にわけるので 2 回変化します
keyframes が 0% と 50% と 100% の 3 つなら変化は 2 つなのでそれぞれ 2 段階で 4 回変化します

サンプルです
<!doctypt html>

<style>
@keyframes anim {
0% { font-size: 10px; color: red; }
25% { font-size: 30px; color: yellow; }
50% { font-size: 50px; color: red; }
90% { font-size: 70px; color: yellow; }
100% { font-size: 90px; color: red; }
}
div{
animation: anim 30s steps(2, start) 0s;
}
</style>

<div>text</div>
アニメーションの変化を表示させます
let pre = null
const time = Date.now()
setInterval(e => {
const current = getCurrentStyle()
if(pre !== current){
const sec = (Date.now() - time) / 1000
console.log(sec, (sec / 30 * 100).toFixed(2), current)
pre = current
}
}, 100)

function getCurrentStyle(){
const style = getComputedStyle(document.querySelector("div"))
return style.fontSize + " " + style.color
}
0.102 "0.34" "20px rgb(255, 128, 0)"
3.801 "12.67" "30px rgb(255, 255, 0)"
7.501 "25.00" "40px rgb(255, 128, 0)"
11.301 "37.67" "50px rgb(255, 0, 0)"
15.001 "50.00" "60px rgb(255, 128, 0)"
21.102 "70.34" "70px rgb(255, 255, 0)"
27.102 "90.34" "80px rgb(255, 128, 0)"
28.603 "95.34" "90px rgb(255, 0, 0)"
30.102 "100.34" "16px rgb(0, 0, 0)"

0.102 秒経過したタイミングがアニメーションが開始されたときです
各行 1 列目が 経過時間で 2 列目がパーセントになります
0% から 25% の間の 12.5% と 90% と 100% の間の 95% のように keyframes の真ん中で切り替わっています

steps で 2 状態の切り替える

steps に 1 を指定すると途中の状態がなく切り替わりました
これと keyframe ごとに steps が効くことを合わせるとこんなことができます

<!doctype html>

<style>
@keyframes anim {
50% { font-size: 50px; color: blue; }
100% { font-size: 90px; color: red; }
}
div{
animation: anim 3s steps(1, start) 0s infinite;
}
</style>

<div>text</div>

2 つの keyframe に指定した状態が交互に表示されます

frames

timing-function には frames というものもあります
まだどのブラウザでも動いてないのですが 仕様の図を見るとフレーム数が指定できて 2 を指定すると 0% と 100% が表示できるようです
steps では 0% と 100% を指定したら 2 つの中間の状態と 100% の状態の切り替わりになっていたので ちょっと面倒なことになりましたが frames だと簡単に解決できそうです

https://www.w3.org/TR/css-timing-1/#timing-functions

steps だと

css-tf-steps

だったのが frames だと
css-tf-frames

になります
0 と状態と 1 の状態が半々で存在するのが frames の利点です