はじめに
つい先日、Google Chrome 111がリリースされ、エンジニアの皆さんはきっと大騒ぎされたことでしょう。
(僕は騒ぎました)
Google Chrome 111の詳細は以下のリンクで確認できます。
中でも最も注目されているのが、『View Transition API』 と呼ばれるSPAの挙動をシームレスに行ってくれる機能です。
ただし、これを実装するには他のブラウザの挙動を考慮する必要があります。
タイトルの通りですが、View Transition APIがSafariなど他のブラウザではどのように振る舞うのかを理解しておく必要があります。
では早速、簡単に実装してみる
View Transition APIを実装します。
今回は実装にフォーカスを当てているわけではないので、1つのファイルの中で簡単に行いたいと思います。
コードを書く前に、以下の記事を参考にしました。この記事は非常にわかりやすい手順を提供しているので、気になる方は読んでみることをお勧めします。
View Transition APIを実装したコード
コードをそのまま拝借した結果が以下です。
サンプルコード(クリックで開く)
<!DOCTYPE html>
<html>
<head>
<style>
.page {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
box-sizing: border-box;
padding: 20px;
}
.top {
background: #fff;
}
.sub1 {
background: #def;
}
.sub2 {
background: #fed;
}
.cat1 {
view-transition-name: cat1;
contain: paint;
}
.cat2 {
view-transition-name: cat2;
contain: paint;
}
</style>
</head>
<body>
<div class="page top" data-page="top">
<h1>Top page</h1>
<ul>
<li>
<a href="#sub1" data-to="sub1">
<img
class="cat1"
src="https://placekitten.com/300/300"
width="64"
height="64"
/>
Sub page 1
</a>
</li>
<li>
<a href="#sub2" data-to="sub2">
<img
class="cat2"
src="https://placekitten.com/400/400"
width="64"
height="64"
/>
Sub page 2
</a>
</li>
</ul>
</div>
<div class="page sub1" data-page="sub1" hidden>
<h2>Sub page 1</h2>
<p>
<img
class="cat1"
src="https://placekitten.com/300/300"
width="300"
height="300"
/>
</p>
<p><a href="#top" data-to="top">Back</a></p>
</div>
<div class="page sub2" data-page="sub2" hidden>
<h2>Sub page 2</h2>
<p>
<img
class="cat2"
src="https://placekitten.com/400/400"
width="300"
height="300"
/>
</p>
<p><a href="#top" data-to="top">Back</a></p>
</div>
<script>
document.querySelectorAll('a[data-to]').forEach((a) => {
a.addEventListener('click', (e) => {
e.preventDefault();
const to = e.currentTarget.dataset.to;
// ページを切り替える
document.startViewTransition(() => {
document.querySelectorAll('[data-page]').forEach((page) => {
page.hidden = to !== page.dataset.page;
});
});
});
});
</script>
</body>
</html>
他のブラウザでの挙動を確認する
結論からお話しすると、「動作しません」でした。
当然と言えば当然ですね。
確認したブラウザは以下の二つです。
- Safari
- Arc
なぜ動かないのか
そもそもsafariでは、トランジション以前にコンポーネントが表示されないですね。
この問題は Safari がstartViewTransition()
というメソッドをサポートしていないために発生します。
document.startViewTransition(() => {
document.querySelectorAll('[data-page]').forEach((page) => {
page.hidden = to !== page.dataset.page;
});
});
リンクを押した時に、hidden が解除されるようにしたいのですが、startViewTransition()
メソッド内でこの処理が行われているため、処理が実行されません。
したがって、View Transition API を実装する場合は、ブラウザがstartViewTransition()
をサポートしているかどうかを確認する必要があります。
対策
前述した通り、ブラウザがstartViewTransition()
をサポートしていることを確認する必要があります。
// startViewTransition()をサポートしている場合
if(typeof document.startViewTransition === "function") {
document.startViewTransition(() => {
document.querySelectorAll('[data-page]').forEach((page) => {
page.hidden = to !== page.dataset.page;
});
});
} else {
document.querySelectorAll('[data-page]').forEach((page) => {
page.hidden = to !== page.dataset.page;
});
}
User-Agent や User-Agent Client Hints は冗長なため、Chrome バージョン 111 以上であることを確認するだけで十分です。
今回は、ブラウザがstartViewTransition()
をサポートしている場合は View Transition API を使用し、そうでない場合は通常の方法でコンポーネントを表示するようにしています。
終わりに
タイトルの疑問に対しての結論ですが、
View Transition APIはChrome以外のブラウザでは認識されず、動きませんでした。
なので、View Transition APIを利用する際には、safariユーザーでも問題なく利用できるように、処理を分岐しておく必要があります。
今のブラウザのシェア率はこうなっているみたいです。
35%の人たちのためにも、Chrome以外でのテストも行っておくのがベターかもしれません。
Github Pagesに公開してみた
このコードを直接確認することもできますが、皆さんにも実際に触っていただきたいと思い、Github Pages にて公開しました。
View Transition API の挙動を確かめ、実際に動かしてみてください。
URL は以下の通りです。
ちなみに、上記の対策を施し済みですので、safariでもちゃんとコンポーネントを呼び出せるようになっています。
修正後のソースコードも置いておきます。
修正後のソースコード(クリックで開く)
<!DOCTYPE html>
<html>
<head>
<style>
.page {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
box-sizing: border-box;
padding: 20px;
}
.top {
background: #fff;
}
.sub1 {
background: #def;
}
.sub2 {
background: #fed;
}
.cat1 {
view-transition-name: cat1;
contain: paint; /* 現在の仕様ではこちらも必要 */
}
.cat2 {
view-transition-name: cat2;
contain: paint;
}
</style>
</head>
<body>
<div class="page top" data-page="top">
<h1>Top page</h1>
<ul>
<li>
<a href="#sub1" data-to="sub1">
<img
class="cat1"
src="https://placekitten.com/300/300"
width="64"
height="64"
/>
Sub page 1
</a>
</li>
<li>
<a href="#sub2" data-to="sub2">
<img
class="cat2"
src="https://placekitten.com/400/400"
width="64"
height="64"
/>
Sub page 2
</a>
</li>
</ul>
</div>
<div class="page sub1" data-page="sub1" hidden>
<h2>Sub page 1</h2>
<p>
<img
class="cat1"
src="https://placekitten.com/300/300"
width="300"
height="300"
/>
</p>
<p><a href="#top" data-to="top">Back</a></p>
</div>
<div class="page sub2" data-page="sub2" hidden>
<h2>Sub page 2</h2>
<p>
<img
class="cat2"
src="https://placekitten.com/400/400"
width="300"
height="300"
/>
</p>
<p><a href="#top" data-to="top">Back</a></p>
</div>
<script>
document.querySelectorAll('a[data-to]').forEach((a) => {
a.addEventListener('click', (e) => {
e.preventDefault();
const to = e.currentTarget.dataset.to;
// ページを切り替える
if (typeof document.startViewTransition === 'function') {
document.startViewTransition(() => {
document.querySelectorAll('[data-page]').forEach((page) => {
page.hidden = to !== page.dataset.page;
});
});
} else {
document.querySelectorAll('[data-page]').forEach((page) => {
page.hidden = to !== page.dataset.page;
});
}
});
});
</script>
</body>
</html>