Rails: Turbo Streamsをスムーズに遷移するturbo-transitionライブラリを公開しました(翻訳)
コンポーネントやパーシャルをDOMに注入したり、逆にDOMから削除したりする動作を、もっと心躍るような感じにできないものかとずっと考え続けていました。
つまり、以下のようにしたいのです。
以前の私は、Turboからのコールバックに頼るソリューションを使っていました。これはこれで使えるのですが、このソリューションも使い方も私にとっては全然嬉しくも何ともありませんでした。
そういうわけで、ここに晴れてturbo-transitionライブラリを紹介したいと思います。
このturbo-transitionは、Turbo FrameとTurbo Streamの忠実な"手下"とも言うべきライブラリで、要素がDOMに出入りするときに要素に遷移エフェクトをかけてくれます。以前私が使っていたものよりずっとシンプルでいながら、強力なソリューションです。
私はRails Designersに積極的に取り組むうちに、興味深いさまざまなテクニッックを編み出してきました。Railsのturbo-frameやturbo-streamが、本質的には定義済みのCSSクラスを追加・削除するWeb標準のカスタム要素に過ぎませんが、私の作ったturbo-transitionもその意味ではturbo-frameやturbo-streamの仲間なのです。
皆さんのRailsアプリに、ぜひ次世代の最新のTurboを搭載してお試しください。
turbo-transitionとカスタム要素について関心のある方は続きをどうぞ。👇
🔗 turbo-transitionのしくみ
turbo-transitionは、突き詰めればただのカスタム要素に過ぎません。独自のHTMLタグを定義してそこに振る舞いを作り込むことで、まるでHTMLが拡張されたかのように使えます。
以下はturbo-transitionの最小限のセットアップです。
class MyElement extends HTMLElement {
// 要素がそのページに追加されると呼び出される
connectedCallback() {
// これでDOMに要素が追加される
}
// 要素が削除されると呼び出される
disconnectedCallback() {
// DOMからクリーンアップされる
}
}
// 登録するとブラウザが新しい要素を認識するようになる
customElements.define("my-element", MyElement);
turbo-transitionはこの基盤の上で、要素がページに追加・削除されるときのアニメーションを処理します。
class TurboTransition extends HTMLElement {
connectedCallback() {
// 要素追加時のアニメーションを処理
}
remove() {
// 要素削除時のアニメーションを処理
}
}
customElements.define("turbo-transition", TurboTransition);
connectedCallbackは、遷移のクラス、タイミング、クリーンアップを管理します。
class TurboTransition extends HTMLElement {
connectedCallback() {
if (this.#config.hasEnterTransition()) {
this.#enter();
}
}
remove() {
if (this.#config.hasLeaveTransition()) {
this.#leave();
return this;
}
return super.remove();
}
}
遷移アニメーションの処理はシンプルそのものです。
async #transition({ to, element = null }) {
const target = element || this.firstElementChild;
if (!target) return;
const classes = this.#config.getClasses({ for: to });
await this.#utilities.run(target, classes);
}
target要素を取得する(turbo-transitionは要素内部に子要素を1個必要とします)- 関連するクラスを属性からフェッチする(
enter-from-class="fade-enter-from"など) - 遷移シーケンスを実行する
remove()メソッドは、元の要素が削除される間にアニメーションが消えないよう、要素を複製してから処理しています。
#leave() {
const clone = this.cloneNode(true);
const parent = this.parentNode;
parent.replaceChild(clone, this);
// Run transition on the clone's content
this.#transition({ to: "leave", element: clone.firstElementChild })
.then(() => clone.parentNode?.removeChild(clone));
}
移行をスムーズにするため、クラスは特定の順序で適用されます。
// utilities.js
async run(element, classes) {
this.#applyInitialState(element, classes); // 'from'と'active'を追加
await this.#nextFrame(); // ブラウザ待ち
this.#applyFinalState(element, classes); // 'to'stateに切り替える
await this.#waitForCompletion(element); // アニメーションの完了待ち
this.#cleanup(element, classes); // クラスを削除
}
このようにして、コンポーネントや要素をスムーズに遷移して、Turbo FrameやTurbo Streamとシームレスに動作する、信頼性の高い方法が生まれました。
ぜひ皆さんもturbo-transitionを試してみて、GitHub Starを押してください!⭐
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。
本記事では、表記をturbo-transitionに統一しています。