この記事はJavaScript 2 Advent Calendar 2019、24日目の記事です。
Svelteを使って、ブラウザで動作するSwipe Card UIを作りました。
スマホにしか対応していませんが、こちらにデプロイしてあるので、興味がある方は触ってみてください。
(ソースコードはgithubで公開しています)
はじめに:Svelteとは
- Write less code
- No virtual DOM
- Truly reactive
の三拍子をアピールポイントにしている、Javascriptフレームワーク(正確にはコンパイラ)です。
最新バージョンはv3。
「React,Vue,Svelte」という立ち位置を目指しています。
日本語の記事を色々探るより、
公式のHPと、作者が登壇した際の動画を見るのが一番分かりやすくて詳しいと思います。
Svelteのコードの書き方の基本については、Write less code
に出てくるサンプルコードを見れば、ひと目わかると思います。
画像になりますが、貼っておきます。
コード量はたしかに少ないですね。
(v2まではかなり書き方が違ったようです。)
この記事で言及すること
今回僕が作ったのは、tinderなどのマッチングアプリでよく見る、Swipe Card UIもどきです。
Svelte流の書き心地を確かめるため、サンプルとして作成してみました。
少々雑ですが、スワイプすれば何枚かごとに写真が切り替わります。
(写真が切り替わる時に、移動中のカードの写真も変わっちゃいますが許して)
この記事では、実際に作成する中で気になった、Svelteの特徴の中から、以下の項目について詳しく記述します。
- DOMイベント/カスタムイベントの管理
- テンプレートでのロジック分岐
- UIのアニメーションに必要な動きの計算
DOMイベント/カスタムイベントの管理
Swipe Card UIでは、ユーザーの操作によるタッチイベントを監視します。
そのイベントに伴い、画面に描画しているカードを移動させる必要があるからです。
SvelteでネイティブDOMイベントを扱うのは簡単です。
例えば、要素をクリックした時にhoge
関数を実行したい場合、以下のようにイベントハンドラを記述します。
<div on:click={hoge}></div>
vueのv-on
デイレクティブとほぼ同じですね。
それと似たようにイベント修飾子も存在していて、
<div on:click|once={hoge}></div>
こんな感じでDOMイベントに制限をもたせたり、オプションを加えることが出来ます。
より詳しくはチュートリアルをご覧くださいませ。
Svelte Tutorial Event modifiers
イベントハンドラについてSvelteで面白と思ったのは、ここに開発者が定義するカスタムイベントを指定できることです。
今回僕が作成したSwipeCardのコードを見ると、以下のようにイベントハンドラを記述しています。
(イベントハンドラ以外は省略)
<div
use:swipe
on:swipestart={handleSwipeStart}
on:swipemove={handleSwipeMove}
on:swipeend={handleSwipeEnd}
/>
ここで設定しているswipestart
やswipemove
は、DOMのネイティブイベントではありません。
これは、use
ディレクティブに指定する関数の中で作成している独自のイベントです。
use
ディレクティブで呼び出しているswipe関数でswipestart
を定義している部分を抜粋すると、以下の部分になります。
export const swipe = node => {
let x;
let y;
const handleTouchstart = event => {
x = event.touches[0].clientX;
y = event.touches[0].clientY;
node.dispatchEvent(
new CustomEvent("swipestart", {
detail: { x, y }
})
);
}
node.addEventListener("touchstart", handleTouchstart);
return {
destroy() {
node.removeEventListener("touchstart", handleTouchstart);
}
};
}
引数で取得するDOMノードに対し、addEventListener
を用いて
ネイティブイベントが呼び出された時に特定のCustomEvent
をdispatchEvent
で発火させています。
関数の返り値として、removeEventLitener
が設定されているので、余計なイベントの監視はありません。
(CustomEvent
やdispatchEvent
はSvelte特有のメソッドではありませんが)
このように独自のイベントを結びつけることができます。
今回のサンプルアプリではそれほど真価を発揮していませんが、
サードパーティのライブラリを結びつけたり、複数のイベントをまとめて管理したい時に
価値を発揮するのかな?と思っています。
テンプレートでのロジック分岐
Svelteで、HTML部分に出し分けを実装する場合は以下のようになります。
{#if boolean}
<p>hoge</p>
{:else}
<p>fuga</p>
{/if}
これはVue,Reactの書き方とも大きく異なります。
HTMLのテンプレートエンジンや、PHPのコードを書く感覚に近いと思いました。
ifブロックの他にも、eachで配列を回したり、awaitを使って表示を出し分けたりするブロックがあります。
ブロックの始まりは#
、継続は:
、終わりは/
でかき分けるのですが、
慣れるまでは違和感を感じそうです。
UIのアニメーションに必要な動きの計算
Svelteでは、UIの異なる2つの状態を滑らかにつなげるための機能があります。
Tweenedと、Springです。
Tweened
を使うとCSSのanimation
プロパティで設定することが多かった
duration
やeasing
の計算をJS内で完結することができます。
Spring
では、変化の「stiffiness(かたさ)」や「damping(跳ね返り)」も含めた定義が可能です。
Swipe Card UIには、Spring
をピンポイントで使用しています。
特に便利だったのは、タップをやめてカードが元の位置に戻る際のdamping
です。
これらの機能が行うのは変数の変化までなので、その値をUIと結びつける必要があるのですが、
今回はインラインstyle内で変数を使用して結びつけました。
おわりに
今回使った機能の他にも、アプリ内でグローバルに値を管理するためのStore、
propsの受け渡しをなくすためのContext APIなど、Svelteのコードにはいくつも特徴的な点があります。
公式のチュートリアルによくまとまっているので、
気になる方は触ってみてください!
個人的に、
Vueが好きな人はとっつきやすいかもしれませんが、
Reactが好きな方にはあまり好かれなさそうだな、と思いました。
ReactやVueと何がいいんだ、という点についての詳細は、
画面遷移や実際にアプリとして使用できる機能を持つwebアプリを作った時に振り返ります。
(サンプルケースだけで比較している記事はよくあるのですが、それだけで実務で通用するか否かは分からないと思っている)
1月中に作れるかな!
進捗は多分、twitterでつぶやくと思います。
ちなみに:コンパイル後の容量
Svelteは、コンパイル後のコードにはvanillaJSしか含まれず、軽量なことをアピールしています。
今回実際に作成したファイルの容量は・・・
25KB!
確かに軽い。