
The Path To Awesome CSS Easing With The linear() Function
記事は上記記事を意訳したものです。
※当ブログでの翻訳記事は元サイト様に許可を得て掲載しています。
私が常に心に残る言葉を言い換えると、「最良のアニメーションは、気づかれないものだ」となります。ウェブ上のモーションデザインで最も重要なコンセプトの一つは、モーションが「正しく感じられる」ことです。同時に、CSSは自然で目立たないユーザー体験を作り出すアニメーションやトランジションの作成において、限られた機能しか提供していませんでした。
幸い、それは変わりつつあります。今日は、CSSに新たに追加されるイージング機能について見ていきましょう。具体的には、CSSのEasing Level 2仕様のエディタードラフトに現在定義されている新しいイージング関数「linear()」のスーパー能力を紹介したいと思います。一緒に、そのカスタムイージングカーブを作成する能力を探り、自然に感じるUIの動きに導く方法を見ていきましょう。
実際にデモを始める前に、私がUIモーションデザイン全般に対するアプローチに大きな影響を与えた記事をいくつかご紹介します:
- 「Good to great UI animation tips」, Pablo Stanley
- 「Disney’s Motion Principles in designing interface animations」, Ruthiran Babu
UIデザインのモーションに関する素晴らしいリソースは他にもたくさんありますが、これらは私がブラウザのブックマークに常に保存しており、この記事にも大きな影響を与えました。
- CSSにおけるイージングの現状
- イージングカーブの可視化
- linear()を使って「特別な」イージングを
- 「そんな数字をすべて計算するのは無理」
- どこに向かっているのか?
- GSAPイージングをCSSコードに変換する方法
CSSにおけるイージングの現状
CSSでのイージングは、アニメーションかトランジションを扱うかによって、animation-timing-function または transition-timing-function プロパティで定義します。
「期間(duration)」はタイミングに関することであり、タイミングは動きの自然さに大きな影響を与えます。
ですが、最近までCSSは次のイージング関数に限られていました:
- linear
- steps
- ease
- ease-in
- ease-out
- ease-in-out
- cubic-bezier()
復習のために、このデモをチェックしてみてください。これにより、車がトラックを進む速度に対する異なるタイミングの影響が示されています。
cubic-bezier()関数は、少しキャラクターを持たせたイージングを作成するために最も柔軟性を提供してきました。サイト cubic-bezier.comは、オーダーメイドのイージング関数を作成するために優れたリソースです。それ以外にも、正確なカーブの値を計算するのは面倒です。
これが現在のCSSでのイージングの状態です。それでは、新しい機能を見ていきましょう。
イージングカーブの可視化
異なるイージングをグラフィカルなカーブで視覚化できます。サイト easings.netは、cubic-bezier()タイミング関数で使用できるオプションを提供しています。

イージングカーブは動きの挙動を表します。左端は動きの開始点であり、青いセグメントは速い動きを示します。
イージングカーブは、Chromium DevToolsでも表示でき、トランジションやアニメーションに適用されたカーブを確認できます。

linear()を使って「特別な」イージングを
もし、既存のイージング関数で物足りなければ、どうすればよいのでしょうか?例えば、バウンスやスプリングのような効果が必要な場合です。これらはcubic-bezier()では達成できないタイプのイージングです。
ここで登場するのが、新しい linear() イージング関数です。これはJake Archibaldによって提案され、CSS Easing Level 2仕様のエディタードラフトで現在定義されています。MDNではこのように説明されています:
linear()関数は、ポイント間を線形に補間する区分線形関数を定義し、バウンスや弾性効果のような複雑なアニメーションを近似することができます。
言い換えれば、カスタムイージングカーブを定義するために、好きなだけポイントをプロットできる方法です。これにより、これまでCSSアニメーションやトランジションではできなかった新しい可能性が開かれます。
例えば、バウンス用のイージングは次のように定義できます:
:root { --bounce-easing: linear( 0, 0.004, 0.016, 0.035, 0.063, 0.098, 0.141 13.6%, 0.25, 0.391, 0.563, 0.765, 1, 0.891 40.9%, 0.848, 0.813, 0.785, 0.766, 0.754, 0.75, 0.754, 0.766, 0.785, 0.813, 0.848, 0.891 68.2%, 1 72.7%, 0.973, 0.953, 0.941, 0.938, 0.941, 0.953, 0.973, 1, 0.988, 0.984, 0.988, 1 ); }
このような動作になります
「そんな数字をすべて計算するのは無理」
確かに、このイージングの例は、まるで空気から取り出されたような数字の羅列に見えます。cubic-bezier()と同じように、最初はかなり複雑に感じます。良い点は、一度イージングを定義したら、再度触れる必要はほとんどないということです…少なくともしばらくの間は。基本的には一度設定したら忘れても大丈夫なものです。
では、最初にその数字をどうやって得るのでしょうか?linear()の背後にいる賢い人物、Jakeがオンラインジェネレーターを作成しており、私たちがその重い作業を全てやってくれます。実際、バウンスのデモで使ったイージング値は、Jakeの便利なツールから直接取得したものです。linear()ジェネレーターで出力されたコードを見てみましょう。

どこに向かっているのか?
長い間、特別なイージングが必要なときには、GreenSockが私の頼みの綱でした。その「Ease Visualizer」は、インタラクティブなドキュメントの中でもお気に入りの一つです。

GreenSockのEase Visualizerでは、異なるイージングカーブ(カスタムカーブを含む)をデモし、そのコードスニペットをライブラリとともに提供してくれます。
linear()関数を聞いた瞬間、私の頭の中で「GreenSockのイージングをCSSに変換できないだろうか?」という考えが浮かびました。JavaScriptを使わずに、直接CSSで人気のあるイージングセットにアクセスできたらどれだけ素晴らしいことでしょう。
GreenSockのビジュアライザーでは、JavaScriptまたはSVGパスを入力として受け付けます。最初に思いついたのは、DevToolsを開いてビジュアライザーからSVGパスを取得し、それをツールにドロップすることでした。しかし、viewBoxの座標を0 0 1 1にスケールダウンする必要があり、この方法ではうまくいきませんでした。
Jakeに連絡を取っていくつか質問をしましたが、最終的には彼がGreenSockのGitHubリポジトリからバックイージング関数を抽出し、私が最初に求めていたイージングを作成してくれました。
GSAPイージングをCSSコードに変換する方法
いくつかの文脈を提供したので、GSAPイージングをCSSコードに変換するために必要なパズルの全ての部分が揃いました。
まず、Jakeのlinear()ジェネレーターツールから必要な部分をスクリプトに抽出します。アイデアとしては、いくつかのキーをループし、linear()イージングを使ったCSSブロックを生成することです。GreenSockには、parseEaseという素晴らしいユーティリティメソッドがあります。これに文字列を渡すと、イージング関数が返されます。受け入れ可能な文字列は、GreenSockのイージング関数です。
const ease = gsap.parseEase('power1.in') ease(0.5) // === 0.25
このように、異なるイージング関数を持つオブジェクトをループし、それをツールから抽出したコードに渡すことができます。その抽出されたコードを、私たちのニーズに合わせて修正します。
const easings = '' const simplified = 0.0025 const rounded = 4 const EASES = { 'power-1--out': gsap.parseEase('power1.out') // 他のイージング関数 } // イージングキーをループして結果を生成する。 for (const key of Object.keys(EASES)) { // イージング関数をlinear-generatorコードに渡す。 const result = processEase(key, EASES[key]) const optimised = useOptimizedPoints(result.points, simplified, rounded) const linear = useLinearSyntax(optimised, rounded) const output = useFriendlyLinearCode(linear, result.name, 0) easings += output } // 出力CSS文字列を生成する。 let outputStart = ':root {' let outputEnd = '}' let styles = ` ${outputStart} ${easings} ${outputEnd} ` // ボディに書き込む。 document.body.innerHTML = styles
このように、linearジェネレーターから抽出した関数はそれぞれ異なることを行います。
processEase
これはprocessScriptDataの修正版です。イージング関数を受け取り、グラフのためのポイントを返します。useOptimizedPoints
この関数は、指定したsimplifiedおよびrounded値に基づいてポイントを最適化します。ここで、JakeからDouglas Peuckerアルゴリズムについて学びました。useLinearSyntax
最適化されたポイントを受け取り、linear()関数のための値を返します。useFriendlyLinearCode
linear()の値を受け取り、そのイージング関数のカスタムプロパティ名を使ったCSS文字列を返します。
これらの関数にはあまり手を加えないようにしていますが、どう動作しているかを理解するために、いくつかの場所にブレークポイントやconsole.infoを挿入して確認することも価値があります。
これらを実行した結果、linear()イージング関数とその値を含むCSS変数を得ることができます。以下は、弾力的(elastic)およびバウンス(bounce)イージングの例です。
:root { --elastic-in: linear( 0, 0.0019 13.34%, -0.0056 27.76%, -0.0012 31.86%, 0.0147 39.29%, 0.0161 42.46%, 0.0039 46.74%, -0.0416 54.3%, -0.046 57.29%, -0.0357, -0.0122 61.67%, 0.1176 69.29%, 0.1302 70.79%, 0.1306 72.16%, 0.1088 74.09%, 0.059 75.99%, -0.0317 78.19%, -0.3151 83.8%, -0.3643 85.52%, -0.3726, -0.3705 87.06%, -0.3463, -0.2959 89.3%, -0.1144 91.51%, 0.7822 97.9%, 1 ); --elastic-out: linear( 0, 0.2178 2.1%, 1.1144 8.49%, 1.2959 10.7%, 1.3463 11.81%, 1.3705 12.94%, 1.3726, 1.3643 14.48%, 1.3151 16.2%, 1.0317 21.81%, 0.941 24.01%, 0.8912 25.91%, 0.8694 27.84%, 0.8698 29.21%, 0.8824 30.71%, 1.0122 38.33%, 1.0357, 1.046 42.71%, 1.0416 45.7%, 0.9961 53.26%, 0.9839 57.54%, 0.9853 60.71%, 1.0012 68.14%, 1.0056 72.24%, 0.9981 86.66%, 1 ); --elastic-in-out: linear( 0, -0.0028 13.88%, 0.0081 21.23%, 0.002 23.37%, -0.0208 27.14%, -0.023 28.64%, -0.0178, -0.0061 30.83%, 0.0588 34.64%, 0.0651 35.39%, 0.0653 36.07%, 0.0514, 0.0184 38.3%, -0.1687 42.21%, -0.1857 43.04%, -0.181 43.8%, -0.1297 44.93%, -0.0201 46.08%, 1.0518 54.2%, 1.1471, 1.1853 56.48%, 1.1821 57.25%, 1.1573 58.11%, 0.9709 62%, 0.9458, 0.9347 63.92%, 0.9349 64.61%, 0.9412 65.36%, 1.0061 69.17%, 1.0178, 1.023 71.36%, 1.0208 72.86%, 0.998 76.63%, 0.9919 78.77%, 1.0028 86.12%, 1 ); --bounce-in: linear( 0, 0.0117, 0.0156, 0.0117, 0, 0.0273, 0.0468, 0.0586, 0.0625, 0.0586, 0.0468, 0.0273, 0 27.27%, 0.1093, 0.1875 36.36%, 0.2148, 0.2343, 0.2461, 0.25, 0.2461, 0.2344, 0.2148 52.28%, 0.1875 54.55%, 0.1095, 0, 0.2341, 0.4375, 0.6092, 0.75, 0.8593, 0.9375 90.91%, 0.9648, 0.9843, 0.9961, 1 ); --bounce-out: linear( 0, 0.0039, 0.0157, 0.0352, 0.0625 9.09%, 0.1407, 0.25, 0.3908, 0.5625, 0.7654, 1, 0.8907, 0.8125 45.45%, 0.7852, 0.7657, 0.7539, 0.75, 0.7539, 0.7657, 0.7852, 0.8125 63.64%, 0.8905, 1 72.73%, 0.9727, 0.9532, 0.9414, 0.9375, 0.9414, 0.9531, 0.9726, 1, 0.9883, 0.9844, 0.9883, 1 ); --bounce-in-out: linear( 0, 0.0078, 0, 0.0235, 0.0313, 0.0235, 0.0001 13.63%, 0.0549 15.92%, 0.0938, 0.1172, 0.125, 0.1172, 0.0939 27.26%, 0.0554 29.51%, 0.0003 31.82%, 0.2192, 0.3751 40.91%, 0.4332, 0.4734 45.8%, 0.4947 48.12%, 0.5027 51.35%, 0.5153 53.19%, 0.5437, 0.5868 57.58%, 0.6579, 0.7504 62.87%, 0.9999 68.19%, 0.9453, 0.9061, 0.8828, 0.875, 0.8828, 0.9063, 0.9451 84.08%, 0.9999 86.37%, 0.9765, 0.9688, 0.9765, 1, 0.9922, 1 ); }
この出力は、異なるキーや精度で調整することができ、非常に柔軟です。実際に、これらのGreenSockイージングをCSSに組み込むことができるのは本当にクールな点です!
自分専用のCSS linear() イージングを取得する方法
こちらは私が作ったツールで、アニメーションタイプを選択し、linear() イージングを適用してその速度を決定することができます。そこで、カードを裏返して生成されたコードを表示・コピーできます。
linear()がブラウザでサポートされていない場合、@supportsを使ってフォールバック値を使用することもできます。
:root { --ease: ease-in-out; } @supports(animation-timing-function: linear(0, 1)) { :root { --ease: var(--bounce-easing); } }
最後に、GreenSockイージング文字列を入力として受け取り、linear()関数に変換するデモもあります。例えば、elastic.out(1, 0.1)と試してみてください!
TailwindでのLinearイージング
Tailwindを使用している方々には、カスタムイージングを拡張する方法もご紹介します。Tailwindの設定で、linear()イージングを簡単に追加できます。
/** @type {import('tailwindcss').Config} */
const plugin = require('tailwindcss/plugin')
const EASES = {
"power1-in": "linear( 0, 0.0039, 0.0156, 0.0352, 0.0625, 0.0977, 0.1407, 0.1914, 0.2499, 0.3164, 0.3906 62.5%, 0.5625, 0.7656, 1 )",
/* 他のイージング */
}
const twease = plugin(
function ({addUtilities, theme, e}) {
const values = theme('transitionTimingFunction')
var utilities = Object.entries(values).map(([key, value]) => {
return {
[`.${e(`animation-timing-${key}`)}`]: {animationTimingFunction: `${value}`},
}
})
addUtilities(utilities)
}
)
module.exports = {
theme: {
extend: {
transitionTimingFunction: {
...EASES,
}
},
},
plugins: [twease],
}
Tailwind Playでこれを実験してみてください。
結論
CSSはこれまで、アニメーションやトランジションのタイミングに関するコントロールを限られた方法でしか提供していませんでした。そのため、欲しい動作を得るためにはJavaScriptに頼らざるを得なかったのです。しかし、CSS Easing Level 2の新しいlinear()タイミング関数のおかげで、これがすぐに変わるでしょう。是非、あなたのデモにこれらのトランジションを取り入れて、どんな動きを作り出すのか見てみたいと思います!
素晴らしい作業を続けてください。 ┬┴┬┴┤•ᴥ•ʔ├┬┴┬┴