SvelteでSwipe Card UIを作ったので、所感をまとめます

この記事は[JavaScript 2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/javascript2)、24日目の記事です。

[Svelte](https://svelte.dev/)を使って、ブラウザで動作するSwipe Card UIを作りました。
スマホにしか対応していませんが、[こちらにデプロイしてある](https://svelte-swipe-card.netlify.com/)ので、興味がある方は触ってみてください。

![Dec-24-2019 09-10-59.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201590/012764f7-91ad-c8ba-e702-c4a0f7352477.gif)


（ソースコードは[github](https://github.com/Spice-Z/svelte-swip-card)で公開しています）

# はじめに：Svelteとは

- Write less code
- No virtual DOM
- Truly reactive

の三拍子をアピールポイントにしている、Javascriptフレームワーク(正確にはコンパイラ)です。
最新バージョンはv3。
「React,Vue,Svelte」という立ち位置を目指しています。
日本語の記事を色々探るより、
[公式のHP](https://svelte.dev/)と、[作者が登壇した際の動画](https://youtu.be/AdNJ3fydeao)を見るのが一番分かりやすくて詳しいと思います。

Svelteのコードの書き方の基本については、[Write less code](https://svelte.dev/blog/write-less-code)
に出てくるサンプルコードを見れば、ひと目わかると思います。
画像になりますが、貼っておきます。

<img width="682" alt="スクリーンショット 2019-12-24 9.01.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201590/48744661-8d01-57dc-4dfb-546219471a58.png">

コード量はたしかに少ないですね。
（v2まではかなり書き方が違ったようです。）

# この記事で言及すること
今回僕が作ったのは、tinderなどのマッチングアプリでよく見る、Swipe Card UIもどきです。
Svelte流の書き心地を確かめるため、サンプルとして作成してみました。

少々雑ですが、スワイプすれば何枚かごとに写真が切り替わります。
（写真が切り替わる時に、移動中のカードの写真も変わっちゃいますが許して）
この記事では、実際に作成する中で気になった、Svelteの特徴の中から、以下の項目について詳しく記述します。

- DOMイベント/カスタムイベントの管理
- テンプレートでのロジック分岐
- UIのアニメーションに必要な動きの計算

# DOMイベント/カスタムイベントの管理
Swipe Card UIでは、ユーザーの操作によるタッチイベントを監視します。
そのイベントに伴い、画面に描画しているカードを移動させる必要があるからです。

SvelteでネイティブDOMイベントを扱うのは簡単です。
例えば、要素をクリックした時に`hoge`関数を実行したい場合、以下のようにイベントハンドラを記述します。

```html
<div on:click={hoge}></div>
```
vueの`v-on`デイレクティブとほぼ同じですね。
それと似たようにイベント修飾子も存在していて、

```html
<div on:click|once={hoge}></div>
```

こんな感じでDOMイベントに制限をもたせたり、オプションを加えることが出来ます。
より詳しくはチュートリアルをご覧くださいませ。
[Svelte Tutorial Event modifiers](https://svelte.dev/tutorial/event-modifiers)

イベントハンドラについてSvelteで面白と思ったのは、ここに開発者が定義するカスタムイベントを指定できることです。
今回僕が作成したSwipeCardのコードを見ると、以下のようにイベントハンドラを記述しています。
(イベントハンドラ以外は省略)


```html
  <div
    use:swipe
    on:swipestart={handleSwipeStart}
    on:swipemove={handleSwipeMove}
    on:swipeend={handleSwipeEnd} 
  />
```

ここで設定している`swipestart`や`swipemove`は、DOMのネイティブイベントではありません。
これは、`use`ディレクティブに指定する関数の中で作成している独自のイベントです。
`use`ディレクティブで呼び出しているswipe関数で`swipestart`を定義している部分を抜粋すると、以下の部分になります。

```js
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部分に出し分けを実装する場合は以下のようになります。

```html
{#if boolean}
  <p>hoge</p>
{:else}
  <p>fuga</p>
{/if}
```

これはVue,Reactの書き方とも大きく異なります。
HTMLのテンプレートエンジンや、PHPのコードを書く感覚に近いと思いました。
[if](https://svelte.dev/tutorial/if-blocks)ブロックの他にも、[each](https://svelte.dev/tutorial/each-blocks)で配列を回したり、[await](https://svelte.dev/tutorial/await-blocks)を使って表示を出し分けたりするブロックがあります。



ブロックの始まりは`#`、継続は`:`、終わりは`/`でかき分けるのですが、
慣れるまでは違和感を感じそうです。


# UIのアニメーションに必要な動きの計算
Svelteでは、UIの異なる2つの状態を滑らかにつなげるための機能があります。
[Tweened](https://svelte.dev/tutorial/tweened)と、[Spring](https://svelte.dev/tutorial/spring)です。
`Tweened`を使うとCSSの`animation`プロパティで設定することが多かった
`duration`や`easing`の計算をJS内で完結することができます。

`Spring`では、変化の「stiffiness(かたさ)」や「damping(跳ね返り)」も含めた定義が可能です。
Swipe Card UIには、`Spring`をピンポイントで使用しています。
特に便利だったのは、タップをやめてカードが元の位置に戻る際の`damping`です。

![Dec-24-2019 09-11-30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201590/ee9968e7-993a-245a-5ee9-6e80a739eaa7.gif)


これらの機能が行うのは変数の変化までなので、その値をUIと結びつける必要があるのですが、
今回はインラインstyle内で変数を使用して結びつけました。


# おわりに
今回使った機能の他にも、アプリ内でグローバルに値を管理するための[Store](https://svelte.dev/tutorial/writable-stores)、
propsの受け渡しをなくすための[Context API](https://svelte.dev/tutorial/context-api)など、Svelteのコードにはいくつも特徴的な点があります。
[公式のチュートリアル](https://svelte.dev/tutorial/basics)によくまとまっているので、
気になる方は触ってみてください！


個人的に、
Vueが好きな人はとっつきやすいかもしれませんが、
Reactが好きな方にはあまり好かれなさそうだな、と思いました。

ReactやVueと何がいいんだ、という点についての詳細は、
画面遷移や実際にアプリとして使用できる機能を持つwebアプリを作った時に振り返ります。
（サンプルケースだけで比較している記事はよくあるのですが、それだけで実務で通用するか否かは分からないと思っている）

1月中に作れるかな！
進捗は多分、[twitter](https://twitter.com/rabspice)でつぶやくと思います。

# ちなみに：コンパイル後の容量
Svelteは、コンパイル後のコードにはvanillaJSしか含まれず、軽量なことをアピールしています。
今回実際に作成したファイルの容量は・・・

<img width="458" alt="スクリーンショット 2019-12-24 9.14.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201590/4b6083cf-a47d-97d9-f703-a11d755905ab.png">


25KB!
確かに軽い。