画像ギャラリーのサムネイル→ライトボックスのズーム
サムネイルをフルサイズのライトボックスへズーム。共通の view-transition-name を使った、iOS/Android ネイティブアプリのゴールドスタンダード写真ギャラリーエフェクト。
Shared Elements
詳細な説明
ネイティブギャラリーパターン
iOS の写真でサムネイルをタップしてみてください。スムーズにモーフィングして画面いっぱいに展開します。Web 上で1要素1行の CSS で再現できます。
<!-- グリッド -->
<button onclick="openLightbox('photo-1')">
<img src="thumb.jpg" id="thumb-1" style="view-transition-name: photo-1">
</button>
<!-- ライトボックス(条件付きでレンダリング) -->
<div class="lightbox">
<img src="full.jpg" style="view-transition-name: photo-1">
</div>
function openLightbox(id) {
document.startViewTransition(() => {
showLightboxFor(id);
});
}
ブラウザは view-transition-name: photo-1 が変更前スナップショット(サムネイル)と変更後スナップショット(ライトボックス)の両方に存在することを認識し、バウンディングボックス・アスペクト比・ソース画像を補間してスムーズなズームを生成します。
アスペクト比の扱い
サムネイルが正方形でフルサイズが 16:9 の場合、モーフは自然にアスペクト比を補間します。アニメーション中の歪みを防ぐには、サムネイルに object-fit: cover、フル画像に object-fit: contain を設定します。ブラウザが fit のクロスフェードを処理します。
高解像度画像のロード
トランジション開始時にフルサイズ画像がまだロードされていない可能性があります。ホバー時にプリロードするか、startViewTransition の前に img.decode() を呼びます:
async function openLightbox(id) {
const fullImg = new Image();
fullImg.src = fullUrl(id);
await fullImg.decode(); // ポップを避けるため decode を待つ
document.startViewTransition(() => showLightboxFor(id));
}
サムネイルへ戻す
閉じるときも逆向きに同じ操作。両要素に同じ名前を付け、コールバック内でライトボックスを隠すだけです。ブラウザがライトボックス画像をサムネイルのバウンディングボックスへモーフバックします。
ユースケース
写真ギャラリー、ズーム付きの商品画像カルーセル、プレイヤーを開く動画サムネイル、小さなプレビューが連続性を保ったアニメーションでフルサイズビューアへ展開する UI 全般。