Vue.jsでiOSみたいなトグルスイッチをつくる

# はじめに
vue.jsを使ったトグルスイッチを作成します．いわゆるかっこいいチェックボックスですね．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/73358/9f1fa21f-3679-bfc1-809b-3fe5b0b5c0fa.png)

# 参考にしたもの

下記ページにJavaScriptを一切使わず，CSSとHTMLのみでトグルスイッチを実現した実装方法が紹介されています．今回はこちらを参考にしつつ，Vue.jsで実装しました．
- 「How TO - Toggle Switch w3schools.com」https://www.w3schools.com/howto/howto_css_switch.asp

# リポジトリ
実装した内容は下記リポジトリにアップロード済みです．ご参考にしていただければ幸いです．

- https://github.com/hiroyky/vue_toggle_switch

# 実装
### テンプレート

HTMLは先程のw3schools.comとほぼ変わりません．checkboxのinputタグは実際には不要ですが，SEO対策として有利に働くかなぁと思って残しておきます．
``@click="onToggled"``でイベントを追加しています．

``` toggle-switch.vue.html
<div class="toggle-switch">
<div class="toggle-switch__switch" @click="onToggled">
    <input type="checkbox" class="toggle-switch__switch__checkbox">
    <div 
     class="toggle-switch__switch__slider" 
     :class="{'toggle-switch__switch__slider--enabled': isEnabled, 'toggle-switch__switch__slider--disabled': !isEnabled}">
    </div>
</div>
<label class="toggle-switch__label"><slot></slot></label>
</div>
```

### スクリプト(TypeScript)
HTMLテンプレートに対する動作を実装します．onToggledイベントでisEnabledのtrue/falseを切り替えます．またtoggledイベントを発火させています．

```TypeScript:ToggleSwitch.vue
import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
@Component({})
export default class extends Vue {
    @Prop()
    protected enabled: boolean;

    protected isEnabled = this.enabled ? this.enabled : false;

    protected onToggled() {
        this.isEnabled = !this.isEnabled;
        this.$emit("toggled", this.isEnabled);
    }
}
```

### スタイル

CSS部分はほぼそのままです．``--enabled``と``--disabled``でON・OFF時のデザインをそれぞれ記述しています．

なおscssで実装しました．

```sass
.toggle-switch {
    &__switch {
        $switch_width: 60px;
        position: relative;
        display: inline-block;
        width: $switch_width;
        height: 34px;

        &__checkbox {
            display: none;
        }

        &--vtouch {
            width: 100%;
            height: 100%;
        }

        &__slider {
            $slider_margin: 4px;
            $slider_size: 26px;
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            bottom: 0;
            right: 0;
            border-radius: 34px;

            &:before {
                content: "";
                position: absolute;
                height: $slider_size;
                width: $slider_size;
                transition: .4s;
                border-radius: 50%;
                background-color: white;
            }

            &--disabled {
                background-color: #ccc;
                &:before {
                    left: $slider_margin;
                    bottom: $slider_margin;
                }
            }

            &--enabled {
                background-color: #2196F3;
                &:before {
                    left: ($switch_width - ($slider_size + $slider_margin));
                    bottom: $slider_margin;
                }
            }
        }
    }
}
```

### 呼び出し
コンポーネントを呼び出します．

```HTML
<toggle-switch>メールで通知する</toggle-switch>
<toggle-switch :enabled="true">電話で通知する</toggle-switch>
```

# スマートフォン対応
基本的にこれだけでも動作しますが，スマートフォン向けに一部改修します．
スマートフォンでもタップすれば動作します．しかしながら，スイッチの部分を横にスワイプした場合，反応しません．反応するようにします．

### ライブラリ導入
[vue-touch@next](https://github.com/vuejs/vue-touch/tree/next)というライブラリを導入します．vue2.x系は2018年5月22日現在nextブランチであるため注意してください．

```
$ npm install --save-dev "vue-touch@next"
```

### コードの改修

v-touchで左右のスワイプイベントを追加します．

HTMLにはv-touchタグを追加し，左右のスワイプイベントを追加しています．

```toggle-switch.vue.html
<v-touch class="toggle-switch__switch--vtouch" @swipeleft="turnOff" @swiperight="turnOn" :swipe-options="{direction: 'horizontal'}">
    <input type="checkbox" class="toggle-switch__switch__checkbox">
    <div 
     class="toggle-switch__switch__slider" 
     :class="{'toggle-switch__switch__slider--enabled': isEnabled, 'toggle-switch__switch__slider--disabled': !isEnabled}">
    </div>
</v-touch>
```

TypeScript側には左右のスワイプイベントに対応するメソッドを追加します．

```TypeScript:ToggleSwitch.vue
import VueTouch from "vue-touch";
Vue.use(VueTouch);

export default class extends Vue {
    protected turnOff() {
        this.isEnabled = false;
        this.$emit("toggled", this.isEnabled);
    }

    protected turnOn() {
        this.isEnabled = true;
        this.$emit("toggled", this.isEnabled);
    }
}
```