Chrome 111 新機能「 View Transitions API 」をReactで実装する
View Transitions API ってなに
Chrome111以降に導入された、DOM変更に伴うアニメーション機能です。
非常に簡単なJSとCSSで滑らかなトランジションを実現することができます。
View Transitions APIには、デフォルトでクロスフェードが実装されており、CSSを使用しなくてもAPIを試すことができます。
Reactで書いてみよう
2023年3月中旬の時点で、JavaScriptで書かれた記事しか見つからなかったため、React+Typescriptで実装する方法を模索しました。
「もっとこうしたらいい..!!」「ここがおかしいよ」など、お気づきの点はコメント大歓迎です。
成果物
スクリーンショット
View Transitions API対応の場合(Chrome111)
画像を切り替える際、クロスフェードになっています。
View Transitions API未対応の場合(Safari)
View Transitions API未対応でも、切り替えは行えます。
Reactデモサイト
無料枠なので若干カクカクするかもしれません。
GitHub
コード全体
App.tsx
import { useState } from "react";
const cdnURL = "https://react-transitions-api-demo.vercel.app/image/";
type Image = {
name: string;
file: string;
};
interface ExDocument extends Document {
startViewTransition?: any;
}
const imageData: Image[] = [
{
name: "チューリップ",
file: "25603452.png",
},
{
name: "スカイツリー",
file: "25447899.png",
},
{
name: "つくし",
file: "25794366.png",
},
{
name: "ネモフィラ",
file: "25839343.png",
},
];
function App() {
const [src, setSrc] = useState(cdnURL + imageData[0].file);
const [text, setText] = useState(imageData[0].name);
const handelClick = (data: Image) => {
const displayNewImage = () => {
setSrc(cdnURL + data.file);
setText(data.name);
};
const doc: ExDocument = document;
// View Transitions API未対応のブラウザの場合
if (!doc.startViewTransition) {
displayNewImage();
return;
}
// 引数にDOM更新用のコールバック関数を渡す
doc.startViewTransition(() => displayNewImage());
};
return (
<div className="App">
<h1>React View Transitions API demo</h1>
<main>
<section className="thumbs">
{imageData.map((data) => {
return (
<a
key={data.name}
href="#"
title={`Click to load ${data.name} in main gallery view`}
onClick={() => handelClick(data)}
>
<img
alt={data.name}
src={cdnURL + "thumb/" + data.file}
width={100}
height={100}
/>
</a>
);
})}
</section>
<section className="gallery-view">
<figure>
<img src={src} />
<figcaption>
<div className="caption-text">{text}</div>
</figcaption>
</figure>
</section>
</main>
<footer className="footer">
<a
href="https://github.com/Nyamadamadamada/React_Transitions_API"
target="_blank"
rel="noopener noreferrer"
>
See source code
</a>
</footer>
</div>
);
}
export default App;
サムネ画像をクリックしたらメイン画像に差し替える
驚くべきことに、たった一行追加するだけでView Transitions APIを使えます!
document.startViewTransition(() => {/* DOM変更Function */});
クリック時、クリックした画像の名前とファイル名を渡し、値を更新しています。
const handelClick = (data: Image) => {
const displayNewImage = () => {
setSrc(cdnURL + data.file);
setText(data.name);
};
const doc: ExDocument = document;
doc.startViewTransition(() => displayNewImage());
};
View Transitions API未対応のブラウザの対応として、startViewTransition
を使わずにdisplayNewImage()
を直接呼び出します。
// View Transitions API未対応のブラウザの場合
if (!doc.startViewTransition) {
displayNewImage();
return;
}
startViewTransitionをそのまま使うと型エラーが発生
TypeScriptのdocumentの中にstartViewTransitionがまだ含まれていないらしく、そのまま使用すると以下のエラーになります。
// Property 'startViewTransition' does not exist on type 'Document'.ts(2339)
document.startViewTransition(() => displayNewImage());
対策として、Documentを拡張し、startViewTransitionをany型にしました。
未対応ブラウザが存在するためundefind許可しています。
interface ExDocument extends Document {
startViewTransition?: any;
}
const doc: ExDocument = document;
// 引数にDOM更新用のコールバック関数を渡す
doc.startViewTransition(() => displayNewImage());
追記 型エラー解消の方法
@sprout2000 さまよりコメントいただき、グローバル空間で型宣言する方法を教えていただきました!
ViewTransitionの型をDocumentに継承
export interface ViewTransition {
ready: Promise<void>;
finished: Promise<void>;
updateCallbackDone: Promise<void>;
skipTransition: () => undefined;
}
declare global {
interface Document {
startViewTransition?: (skipTransition) => ViewTransition;
}
}
TypeScript設定ファイルにグローバル型定義を追加
{
"include": ["src","global.d.ts"],
}
documentのまま使える!!
const handelClick = (data: Image) => {
const displayNewImage = () => {
setSrc(cdnURL + data.file);
setText(data.name);
};
// View Transitions API未対応のブラウザの場合
if (!document.startViewTransition) {
displayNewImage();
return;
}
// 引数にDOM更新用のコールバック関数を渡す
document.startViewTransition(() => displayNewImage());
};
@sprout2000 さまコメントありがとうございました!
最後に
簡単にかっこいいアニメーションが作れるのは最高にクールですね!
まだ対応していないブラウザがありますが、どんどん使っていきたいです。
今回は試していませんが、useRefを使って、特定の要素だけトランジションをかけたり、CSSアニメーションゴリゴリ書いても面白そうだなと思いました。
きっと誰かがやってくれるハズ!
参考
Discussion
とても有用な記事をありがとうございます。
ご存知ではないのですが MDN 見ながら雰囲気で書いたらなんか動いちゃってますね...
たぶん間違ってると思いますが...
コメントいただきありがとうございます!!
かっこいい型使いでトランジションできて感動です✨
内容を記事に追記させていただきましたm(_ _)m
やはり Vite.js 環境のおかげでたまたま動いていただけのようです。
スクショは parcel 環境
結局、コールバックの戻り値までは推測できないためこうなりました:
ただ、React で使うのであれば、結局は
setState
メソッドをラップすることになるのでvoid
に決め打ちしても良いかもしれませんね。