Vue.js で実現するマイクロインタラクション

# この記事で分かること
* マイクロインタラクションの概要
* Vue.jsを使ったマイクロインタラクションの実装例  

# マイクロインタラクションとは  
webやスマホアプリに限らず、何らかの製品やサービスのUXを改善する上で、
マイクロインタラクションの重要性が再認識され始めています。

**マイクロインタラクションそのものは新しい技術や概念ではありませんが、
「少ない工夫でUXを劇的に変化させるデザイン手法」として、
マイクロインタラクションは様々なところでそのサービスの評価に大きく影響します。**

この記事では、マイクロインタラクションの簡単な紹介と、
webサービスにおける実装例をvue.jsで紹介します。


## マイクロインタラクションの定義
「マイクロインタラクション」という言葉はDan Suffer氏の著書
「マイクロインタラクション　UI/UXデザインの神が宿る細部」が出典だと思われます。
本書ではマイクロインタラクションを以下のように定義しています。
> **マイクロインタラクションとは、ある作業をひとつだけこなす**
> **最小単位のインタラクション(機器とのやりとり)のことです**

言葉の意味が広いので、身の回りのあらゆる動作がマイクロインタラクションだと言えます。

* スイッチを押すと、部屋の電気が点く。
* スマートフォンのボタンを切り替えると、アイコンが画面に表示され、マナーモードになる。
* ダウンロードボタンを押すと、残り時間がポップアップで表示され、完了したらポップアップが閉じる。
* トースターにパンを入れて開始ボタンを押すと、焼き始め、完了したら食パンが飛び出す。

Dan Suffer氏は、
このような様々な動作を「マイクロインタラクション」と名付けて定義し、
実例を踏まえながら、良いUI/UXデザインを設計するための論理的な考え方をまとめています。

これらはWebサービスやスマホアプリを作る上でも共通して使える考え方ですし、
デザイナー以外の開発や企画の方も知っておくと様々な場面で役に立つでしょう。

## マイクロインタラクションの構成要素
マイクロインタラクションは4つの構成要素から成り立ちます。
簡単に説明と設計時の注意点の一例を紹介します。

### ①トリガー
マイクロインタラクションの動作のきっかけになるもの。
手動トリガー（ユーザによって起動される）と、
システムトリガー（システムによって起動される）の2種類に分類される。

※ユーザから見て何のトリガーなのか直感的に分かることが望ましい。

### ②ルール

トリガーによってマイクロインタラクションが開始されたとき、
どのような一連の処理/動作が行われるかを定義したもの。

※複雑でなくユーザによる制御は最小限にとどめることが望ましい。
※同じトリガーはいつも同じ動作をすることが望ましい。
※トリガーに見合った動作をすることが望ましい。

### ③フィードバック

機器やアプリのルールに関するユーザの理解を助ける
視覚的、聴覚的、触覚的な要素。

※ユーザに必要な情報のみをフィードバックとすることが望ましい。
※何が起こっているのか直感的に把握できるものが望ましい。

### ④ループとモード
各マイクロインタラクションのメタルール。

* ループ：その動作を繰り返し行った場合の定義。
* モード：ルールが2股以上に分岐する場合の定義。

※モードが増えるとユーザは理解しづらくなるので、モードは極力少ないほうが望ましい。




# Vue.js を使ったフィードバックの実装例  

今回はWebページ上でよくあるマイクロインタラクションを
Vue.jsで実装してみました。

Vueは他のJSフレームワークに比べて記述量が少なく済むこと、学習コストが比較的少なかったことが選定理由ですが、
コンポーネント単位で再利用が容易なので、こういったWebページ上のマイクロインタラクションとは相性がよいと思います。

**※本記事では Vue.js の各ディレクティブの詳細の説明は行いません。詳しくは [Vue.js の公式サイト](https://jp.vuejs.org/v2/guide/)を参照してください**

## パスワード入力  

### **動き**

![パスワード入力デモ.gif](https://qiita-image-store.s3.amazonaws.com/0/153290/19b8c116-17a8-ad33-1944-ed850f566d32.gif)

### **コード**
https://jsfiddle.net/tzbgkd7o/9/

### **解説**  

この例では、パスワードの入力をトリガーとして
入力された文字数に応じて
パスワードの強度を文字・色・バーの長さをフィードバックとして返します。

ルールとしては
3文字以下は強度：弱
4~6文字は強度：中
7文字以上は強度：強

としています。


vue.jsでは色々な方法で実現可能ですが、かなり少ないコード量で実現できそうです。
今回は監視プロパティのwatchを使って、テキストボックスの入力があるたびに処理が走ります。

```js:入力のたびに走る処理  
    watch: {
        // この関数はinputが変わるごとに実行される
        input: function (newinput) {
        //入力文字数を取得して、CSSに反映
        var vm = this;
        length = vm.input.length;
        this.barWidth = 60 + length * 15
        
          if (length < 4) {
            vm.message = '弱';
            vm.barColor = '#ff1919';
          }else if (length >= 4 && length <= 6) {
            vm.message = '中';
            vm.barColor = '#ffee33';
          }else{
            vm.message =  '強';
            vm.barColor = '#40ffd9';
          }
          
        }
    }
``` 


文字の反映はバインド変数{{message}}をhtml側に表示させるだけで変更されます。

```html:バインド変数の表示とスタイルの指定 
    <div class="progress-bar" :style="{ background: barColor, width: barWidth + 'px'}">強度:{{message}}</div>
```

バーの長さと色の変更はv-styleディレクティブを使います。コードのように:styleと省略可能です。
複数のスタイルを変更する場合カンマ区切りで指定可能です。

cssの変化が滑らかになるように、
css側のtransitionプロパティにバーのwidthとbackgroud-colorを指定しています。
Vueによってstyleの値が変更されると自動的に滑らかな反映を行ってくれます。  

```css:滑らかな動きはcssで実装
.progress-bar {
  transition: width 0.4s linear,
              background-color 0.4s linear;
  height: 25px;
  border-radius: 3px;
}
```  

## インクリメンタルサーチ  

### **動き**

![incrementalsearch.gif](https://qiita-image-store.s3.amazonaws.com/0/153290/35aeb752-ee1e-7987-b165-6d30d633253d.gif)

### **コード**
https://jsfiddle.net/gkurwnwr/

### **解説**  

まず画面表示の html 部分の全体像を見てみましょう。  

```html:検索結果絞り込み表示全体像  
<div id="incremental-search">
    <ul>
    You chooosed <span v-html="choosed"></span>
    </ul>
    <ul>
    <input type="search" placeholder="Search" v-model="keyword"/>
         <li v-for="item in result">
            <div class="search_item" @click="choosed = item" v-html="item"></div>
    </ul>
</div>
```  

検索部分は input 要素にユーザーが入力した文字列で絞り込みを行い、li 要素で列挙する構造になっており、ユーザーがクリックで選択した要素に応じて、上部の ul 要素内の "You choosed nothing" の部分が書き換わるようになっています。

**検索キーワードとプロパティのバインディング**

```html:検索キーワード部分  
<input type="search" placeholder="Search" v-model="keyword"/>
```

ユーザーが検索窓に入力した文字列は **v-model** で keyword プロパティと双方向のデータバインディングを実現します。   
これによってユーザーの入力に対して、リアクティブに Vue のプロパティを更新できます。  

**配列プロパティの列挙表示**

```html:検索結果絞り込み表示部分  
<li v-for="item in result">
   <div class="search_item" @click="choosed = item" v-html="item"></div>
```  

特定のリストプロパティを列挙するのは **v-for** を使用しています。  
result というプロパティの各要素 item に対して、以降の div タグの描画を行ってくれます。

**絞り込み結果をリアクティブに取得**

```js:js部分  
new Vue({
  el: '#incremental-search',
  data: {
    list: [
      {name : 'Articuno'},     
      {name : 'Zapdos'},
      {name : 'Moltres'},
      {name : 'Mewtwo'},
      {name : 'Mew'},
      {name : 'Pikachu'},
      {name : 'Lugia'},
      {name : 'Celebi'},
      {name : 'Burmy'},
      {name : 'Timburr'}      
    ],
    keyword : '',
    choosed : 'nothing'
  },
  computed:{
    result: function(){
       // 処理省略(list に対して keyword フィルタリング等の処理を行って結果を return する)
    }
  }
})
```  

キーワードによる絞り込み結果は **算出プロパティ** を用いて取得します。  
次の例では result プロパティによってフィルタリング後のリストを取得することができます。  
算出プロパティでは result の値が依存する他のプロパティ（この場合だと keyword ）を watch することで、プロパティの変更を検知して値を更新します。逆に一度計算した result の値は result が依存する他のプロパティが変更されない限りキャッシュされ、再計算されません。 
 
（算出プロパティの詳細は[こちら](https://jp.vuejs.org/v2/guide/computed.html)を参照してください）

**プロパティを HTML として展開**  

```html:絞り込み後のリストの各要素展開部分  
<div class="search_item" @click="choosed = item" v-html="item"></div>
```  

検索キーワードに hit する部分だけにスタイルを当てたいので、絞り込みで取得できるプロパティの一部を span タグで囲むことにします。  

その際、mustaches (二重中括弧による展開) ではプレーンなテキストとして展開されてしまうため、**v-html** を使用します。  

**展開した各要素のクリック時の挙動をバインド**  

折角検索結果を絞り込めたので、絞り込み結果の任意の要素をクリックしたときに何かしらリアクションがあるようにしたいものです。  

今回は **v-on:click** を使用して、要素をクリックした際に choosed プロパティにクリックした要素が set されるようにしました。(choosed プロパティは "You choosed nothing" の notihng 部分を書き換えるのに使われます。)  

更に、**v-on** と **v-bind** はそれぞれ省略記法が存在するため、今回は省略記法を使いました。  
v-on:click の場合、＠click と記述すれば良いです。  

**※css 等は見栄えの問題で筆者がスタイルを当てているだけなので、処理自体に影響はありません** 

# 今後実装してみたいマイクロインタラクション  
* ダウンロード中のフィードバック（進捗バー等）
* ボタン ON/OFF 切り替え時の UI の変化  
* 音楽のボリュームの増減時の UI の変化