概要
SvelteKit2.27で追加されたRemote functionsのformで記事を一件追加します。
追加が終わったら記事の一覧ページに遷移し、追加された記事をViewTransition APIでいい感じにフェードインさせます。
リポジトリ
デモ
- 記事の配列を格納する$stateを用意しておきます
-
/formでフィールドにタイトルを入力すると記事のプレビューが表示されます - フォームをsubmitし記事を追加すると
/に遷移します -
/formから/に記事を投稿して遷移した時だけアニメーションします
おことわり
ViewTransition APIで遊んでみたかっただけの記事です。
ViewTransition API
SvelteKitでは+layout.svelteに以下のように指定することでViewTransition APIを使用できます。
<script lang="ts">
import { onNavigate } from '$app/navigation';
onNavigate((navigation) => {
if (!document.startViewTransition) return;
return new Promise((resolve) => {
document.startViewTransition(async () => {
resolve();
await navigation.complete;
});
});
});
</script>
アニメーションはdata-view-transition-name="post"を指定した記事に対して行います。
Remote functions form
createPost.enhanceでformにenhanceを実装します。
createPostから新規追加された記事のidとtitleをreturnすると、createPost.resultで受け取ることができます。
addPostで新規記事を$stateに追加し、gotoで/に遷移します。
<form
{...createPost.enhance(async ({submit}) => {
await submit();
if (result) {
addPost(result);
goto('/');
}
})}
>
/formのプレビューと/の記事の一つ目には同じdata-view-transition-name="post"が付与されているのでいい感じにアニメーションでフェードインします。
おわり
ページ遷移前にあった要素がページ遷移後のページに向けて移動するのがおもしろいですね。
記事投稿ページをdialogなどで実装するとフロントエンドが余計な複雑さを帯びるのが嫌でページを分けることが多いのですが、体験としてはdialog的なフェードインアニメーションがあった方がいいように感じるので視覚的にdialogっぽく見せるけどページは別れている みたいな用途で使ったりもできるのかなと思っています。
とは言え対応ブラウザがまだまだ…といったところなので一旦ユーザーの使用するブラウザのバージョンが上がってくるのを待っています。
注意
enhanceでgotoしているので、JavaScriptが無効な状態では記事投稿あとそのページに留まります。
本来createPostでreturnせずredirectした方がいいです。
デモ環境ではDBを用意するのが面倒だったのであえて$stateを更新するように作っており、この関係でenhanceを使用しています。(注意!サーバーサイドで$stateを使用してはいけません)
DBなどからデータを引っ張ってくる場合は記事一覧を取得するgetPostsのような関数をRemote functionsのqueryで作成しておき、createPostでgetPostsを更新すると同じようにアニメーションします。
また、resultが存在するときはフォームを表示せず/へのリンクを表示するのもありだと思いますが、
現状Remote functionsのresultがページ遷移で破棄されないバグがあるのでやむを得ず上記の実装を行っています。
resultが更新されないバグには少し困っていますが、これを呼び出しているページへのリンクにdata-sveltekit-reloadを指定することで無理やり解決しています。