デザインのエッジケースとCSSアニメーションを、実装から考える

デザインのエッジケースを協働で解決する方法と、CSSアニメーションのパフォーマンスを計測・改善する方法をまとめます。

Published
2023年6月26日
Read in English

デザインどおりに作ったカードへ、APIから縦長の画像と200文字の説明文が入ってくる。Figmaのワイヤーフレームには、その場合の指定がありません。

フロントエンド開発から学んだことを登壇で話しました。スライドに載せきれなかった背景と参考資料を、記事として残します。

デザインのエッジケースに対処する

Figmaなどのワイヤーフレームだけでは、実装を判断できないことがあります。デザインは代表的な画面サイズとデータで作られるため、極端に長い文字列、縦横比の違う画像、小さな画面などの扱いが明示されていない場合があります。

実装者が推測して補った結果、デザインの意図とずれ、後から修正になることもあります。これはどちらかの不足というより、静的なデザインと動的なプロダクトの間にある情報を、まだ共有できていない状態です。

APIから受け取ったデータでカードを表示する例を考えます。左端が代表的な状態だとしても、画像の縦横比が違う場合や、想定より長い文章が入る場合があります。

not good wireframe card

画像は、表示領域を優先するなら object-fit: cover で一部を切り取り、画像全体を優先するなら object-fit: contain で余白を許容できます。どちらが正しいかは、画像の内容とプロダクトの目的によって変わります。

文章の長さを揃えるなら line-clamp で表示行数を制限できます。ただし、省略によって必要な情報が失われないか、全文へアクセスする手段があるかも確認します。全文を表示するなら、長い単語やURLがカード幅を越えないよう、折り返しも設計します。

ここで挙げた値は「不適切なデータ」ではありません。システムが受け付ける以上、UIが扱うべき入力です。アップロード時に画像や文字数を制限する方法もありますが、制限がユーザーの目的を妨げないかを合わせて考えます。

こうしたエッジケースは、デザイナーとエンジニアが実際の表示を見ながら決めてきました。object-fit の違いは画面を共有して比較し、line-clamp の扱いは具体例を添えてSlackで合意する、といった進め方です。

1スプリントであらゆるケースを完成させようとせず、まず代表的な状態を動かし、レビューで見つかった課題を次の改善へつなげることもあります。ただし、アクセシビリティやデータ損失など、後回しにできない要件は最初から満たします。

より円滑なコミュニケーションを求める

エンジニアとデザイナーは異なる専門性を持ちますが、よい体験を作るという目的は共有できます。違いは障壁ではなく、互いの判断材料を持ち寄るための出発点です。

エンジニアはHTMLやCSS、ブラウザの制約から実装を考えます。デザイナーが持ち込むのは、視覚原則、ユーザー調査、ビジネス要件です。判断の背景を共有できれば、制約が見つかっても、意図を保った代案を探せます。

ユーザーが入力するデータを表示する箇所では、長い文章、空の値、異なる画像比率、エラーなどの代表的な状態をデザインへ含めます。チェックボックスやリスト記号も、独自の意匠が必要なのか、ブラウザ標準でよいのかがわかるだけで、実装時の迷いを減らせます。

エンジニア側も、「この場合のデザインをください」と返すだけでなく、実現可能な選択肢とトレードオフを添えます。たとえば covercontain の表示例を用意すれば、デザイナーが判断しやすくなり、次から使える共通の基準も作れます。

プロダクトは誰か1人の成果物ではありません。実装とデザインの境界にある曖昧さを、対話と具体例で共通認識へ変えていくことが大切です。

User Experience の改善

前半は Core Web Vitalsから調整する画像の改善 と重なるため省略します。Lighthouseの指摘は結果だけを追わず、Chrome DevToolsで原因を測りながら直します。ここでは、後半で話したアニメーションの例を紹介します。

アニメーションのパフォーマンス

仕事で実装したアニメーションが、ある端末では滑らかに動きませんでした。私の環境では再現しないので、感覚ではなく計測から始めます。以下の画像とコードは、同じ現象を別の環境で再現したものです。

Chrome DevToolsのPerformanceパネルには、処理時間と実行内容が並びます。Summaryで時間を確認し、Event Logで何が起きたかを追います。

Chrome DevTools Performance Summary

CSSアニメーションは、スタイル計算、Layout、Paint、Compositeといったレンダリング工程へ影響します。一般に transformopacity はLayoutを発生させず、合成だけで更新できる可能性が高いため、動きのあるUIで使いやすいプロパティです。ただし、必ず独立したレイヤーになるとは限らず、端末や要素の状態にも左右されるため、実際の画面で計測します。

次のように left を変えるアニメーションはLayoutやPaintを繰り返し発生させる可能性があります。

.container {
  position: absolute;
  width: 240px;
  height: 240px;
  background-color: rebeccapurple;
  animation: slide 3s infinite;
}

@keyframes slide {
  from {
    left: 100%;
  }
  to {
    left: 0;
  }
}

実際の Event Log は下記になります。re-paint が頻発しているのがわかると思います。

Chrome DevTools Performance Summary

それ以外の要素がほとんどなければ問題ないかもしれませんが、他の要素や処理がたくさんあると確実にパフォーマンスの悪化につながるでしょう。 それに対して transform を利用した場合はPaint 処理が最小限に抑えられます。

@keyframes slide {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

実際の Event Log でも最小限の re-paint で済んでいます。

Chrome DevTools Performance Summary

ブラウザが要素を独立した合成レイヤーとして扱える場合、ほかの要素を再描画せずに位置や透明度を合成できます。これが transformopacity のアニメーションが滑らかになりやすい理由です。

ただし、レイヤーを増やせば増やすほど速くなるわけではありません。各レイヤーはメモリを消費し、管理や合成にもコストがかかります。will-change で大量の要素を先に昇格させることも避けます。必要なアニメーションに絞り、対象端末で計測し、prefers-reduced-motion による動きの軽減も用意します。

まとめ

無理くりまとめてしまうと、

  • Lighthouse の指摘事項は修正できる段階から対応する
    • ここで知識をつければその後のコーディングに活用できる
  • DevTools でパフォーマンスを測定してから、悪い箇所を修正する
    • アニメーションはレイヤを活用すると良いけど多用注意
    • will-change は本当に最終手段

さいごに

画像をどう切り取るかにも、アニメーションをどう作るかにも、万能な正解はありません。具体例を見ながら話し、実際の端末で測り、目的に合う落としどころを探す。その反復が、プロダクトの品質を少しずつ高めます。

参考資料