CLS（Cumulative Layout Shift）

# CLS（Cumulative Layout Shift）
## とは何か
[`CLS（Cumulative Layout Shift）`](https://web.dev/lcp/)とは、ページのライフスパン全体において発生する予期せぬ`レイアウトシフト`ごとの、`レイアウトシフトスコア`の最大バーストの計測値、[とのこと](https://web.dev/cls/#what-is-cls)。（なるほど、わからん）

（レイアウトシフトがどんな現象かについては、[この記事で紹介されてる動画](https://web.dev/cls/#:~:text=Most%20of%20the%20time%20these%20kinds%20of%20experiences%20are%20just%20annoying%2C%20but%20in%20some%20cases%2C%20they%20can%20cause%20real%20damage.)[^1]がわかりやすい）

[^1]: キャンセルしようと「いいえ、戻る」を押したつもりが、レイアウトシフトが起きて「はい、注文します」を押してしまい、あばばばばばば！（→注文完了）となってしまうの図

`レイアウトシフト`（後述）の発生は、すくなくともユーザへ不快感を抱かせる要因になりうるばかりか、ECなどの業種によってはカスタマートラブル等のビジネス上の大きなトラブルにすら発展しうるリスクを孕んだUI上の問題だったりする。
そしてもちろん、`レイアウトシフト`はパフォーマンス低下要因そのものでもあるため、離脱率やコンバージョンの悪化にも直結する。
（技術的にもビジネス的にもリスクが過小評価されているorそもそも認識すらされてないのかもしれない。）

これらを発生させる原因として、画像などのリソースが非同期に（というか画像の読み込みは大体非同期だが）読み込まれたり、既存のコンテンツの上部に動的にDOM要素が追加されたり、などがあり、これらが原因となって「コンテンツの位置が不意にずれること」を`レイアウトシフト（Layout shift）`という。
`レイアウトシフト`を起こす犯人としては、（画面上の）大きさがロードするまで分からない画像や動画、デフォルトフォントと（同じフォントサイズでも描画上の）大きさが異なるフォント、あるいはサードパーティの広告やウィジェットなどが挙げられる。

そして[`CLS（Cumulative Layout Shift）`](https://web.dev/lcp/)は、この`レイアウトシフト`がページ内でどれだけ起きているかを計測している指標である。

`CLS`の目標スコアとしては、**0.1以下**が[望ましいとされる](https://web.dev/cls/#what-is-a-good-cls-score)。
また、このタイムをほとんどのユーザで体験できることを目指すのであれば、ページロードにおいて75[パーセンタイル](https://www.google.co.jp/search?q=%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%AB)に当たるところが良い閾値となるとのこと。

## もっと詳しい話
### なんで`レイアウトシフト`すぐ起こしてしまうん？
web.devの`CLS`解説記事では、以下のように[説明して](https://web.dev/cls/#:~:text=What%20makes%20this,delay%20isn%27t%20noticeable.)いる。
> What makes this issue even more problematic is that how a site functions in development is often quite different from how users experience it. Personalized or third-party content often doesn't behave the same in development as it does in production, test images are often already in the developer's browser cache, and API calls that run locally are often so fast that the delay isn't noticeable.

_この問題を困難にしている要因は、サイトの機能がしばしば開発時とユーザが体験するものとで大きな差異があることである。パーソナライズされたもしくはサードパーティのコンテンツはしばしば開発時と本番とで同じではないし、テスト画像は大体既に開発者のブラウザのキャッシュ上にあり、そしてローカルで動作するAPIの呼び出しは目立った遅延もなくとても高速だ。_

つまり、開発時点で`レイアウトシフト`の大きさやタイミングについて正確に（計測・）把握しないまま本番に出てしまい、それによって「こんなはずじゃなかった」悪いユーザ体験をもたらしてしまっている、と言える。

### `セッションウィンドウ（session window）`という概念
`レイアウトシフト`は、可視要素がその描画されたレンダリングフレームから位置を変更されればいつでも発生する。
そのため`CLS`では、`session window`というのをつかって`レイアウトシフト`を計測している。

`session window`とは、連続して発生するデータをある短い時間ごとに塊として考える手法で、データが発生し始めてから（最大時間を超えない限りで）その連続発生が収まるまでの間を1つの「ウィンドウ」としてグループ化し、そのウィンドウごとに処理や分析をする手法やそのウィンドウ自体のこと。

`CLS`では、1つ以上の個別の`レイアウトシフト`が各シフトの間に1秒未満、合計ウィンドウ期間で最大5秒の間ですばやく連続して発生した時に「レイアウトがバーストした」とみなし、これを`session window`として扱う。

そして`CLS`の計測対象である「（`レイアウトシフト`の）最大バースト」とは、ウィンドウ内のすべての`レイアウトシフト`を累積したスコアが最大な`session window`を指す。つまりこの最大値を計測した`session window`の`レイアウトシフトスコア`が`CLS`の計測値になる。

### `レイアウトシフトスコア（Layout shift score）`について
`レイアウトシフトスコア（Layout shift score）`とは、変化を起こした要素が「どれだけの画面内で変化のインパクトを発生させたか (`impact fraction`)」と「どれだけの画面内を移動したか (`distance fraction`)」の積を取る。

```
layout shift score = impact fraction * distance fraction
```

#### `impact fraction`について
`impact fraction`とは前述の通り、ある2つの描画フレーム間で画面範囲（ビューポート）領域内に与えたインパクトを計測したもの。

具体的には、全ての不安定(unstable; レイアウトシフトの原因)な要素の、現在の描画フレームからみた前回の描画フレームからの可視領域の和集合のビューポート全域にしめる割合（分数:fraction）が現在のフレームの`impact fraction`に当たる。

例えば、1フレーム前に画面の上から半分（50%）を占めていた要素が、現在のフレームでは上から25%の位置まで下がってきたとすると、そのとき要素の両フレーム間での和集合（両方の画面を半透明にしてピッタリ重ねたときようなイメージ。全くカブってなかったらただの面積の和）は画面全体75%（そのうち50%がカブってる）になるので、そのとき（現在フレームの）`impact fraction`は`0.75`となる。

```
1つ前のフレーム
┏━━━━━━━━━━━━━━━━━┓
┃ ┌─────────────┐ ┃
┃ │             │ ┃
┃ │  unstable   │ ┃
┃ │             │ ┃
┃ └─────────────┘ ┃
┃                 ┃
┃                 ┃
┃                 ┃
┃                 ┃
┗━━━━━━━━━━━━━━━━━┛

現在のフレーム
┏━━━━━━━━━━━━━━━━━┓
┃ ┌-------------┐ ┃
┃ │             │ ┃
┃ ┌─────────────┐ ┃
┃ │             │ ┃
┃ │  unstable   │ ┃
┃ │             │ ┃
┃ └─────────────┘ ┃
┃                 ┃
┃                 ┃
┗━━━━━━━━━━━━━━━━━┛

↑↑の和集合
┏━━━━━━━━━━━━━━━━━┓
┃ ┌─────────────┐ ┃
┃ │             │ ┃
┃ │  union      │ ┃
┃ │   of        │ ┃
┃ │  unstable   │ ┃
┃ │             │ ┃
┃ └─────────────┘ ┃
┃                 ┃
┃                 ┃
┗━━━━━━━━━━━━━━━━━┛
```

#### `distance fraction`について
`distance fraction`とは前述の通り、ある2つの描画フレーム間でビューポート領域内を移動した距離を計測したもの。

具体的には、いずれかの不安定(unstable; レイアウトシフトの原因)な要素が移動した距離をビューポートが横長なら`水平移動距離 / ビューポートの幅`、縦長なら`垂直移動距離 / ビューポートの高さ`）で算出される割合（分数:fraction）が現在のフレームの`distance fraction`に当たる。

前出の例で言うと、ビューポートは縦長なので高さを基準に、移動距離は25%なので、このとき（現在フレームの）`distance fraction`は`0.25`となる。

そして、`impact fraction`の`0.75`と`distance fraction`の`0.25`の積を取って、`0.75 * 0.25 = 0.1875`がこのとき（現在フレームの）`レイアウトシフトスコア（Layout shift score）`となる。

より詳細な条件での計算例は、web.devの`CLS`説明記事の[「Layout shift score」の項](https://web.dev/cls/#layout-shift-score)を参照のこと。

## 2021年1月以前の古い`CLS`実装について
かつて`CLS`はページのライフスパン全体における全てのレイアウトシフトの合算値を計測されていたり、その元である`レイアウトシフトスコア`についても`impact fraction`のみに基づいて計測されたりしていた。
しかし、2021年1月に`CLS`の実装が計測方法に変更されたため、それ以前に言及された`CLS`の実装についての説明記事・動画等は内容が正確でない可能性がある。
仕様変更の詳細な理由などについては、web.devの[Evolving the CLS metric](https://web.dev/evolving-cls)の記事を参照のこと。

## 参照
- [Cumulative Layout Shift (CLS) - web.dev](https://web.dev/cls/)