世界中の人とミーティング時間を決める時に便利なやつ

# スクリーンショット

なにはともあれスクリーンショット

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32540/9a14089c-b3e6-48fd-1551-86c630d46377.png)

タイトルの通り、時差を確認しながらミーティング時間の候補を選ぶことのできるSPAを作ってみたので、その時に調べたり、考えたりしたことを書いてみることに。

各種ライブラリに頼って簡単に作るつもりでしたが、こんなシンプルなものでも動くものを書くにはそれなりにやることあるねぇ... という感じでした。勉強になった。

# デモ

1. 都市をいくつか選んで
2. でてくるタイムラインからいくつか時間を選んで
3. 右下のボタン押したら、ミーティング候補を見れて、コピペもできます

### ライブデモ
[ライブデモ](https://narutaro.github.io/world-timeline/index.html)はこちら

# やったことをつらつらと
今回調べたり、ハマったりした出来上がるまでの過程をつらつらと書いていきます。

## 国や都市の情報

### 都市データ
国名・都市名・規模(人口)の情報は、[ここ](https://github.com/kevinroberts/city-timezones)の情報をつかわせていただきました。人口○万人以上みたいな感じで主要な都市だけをフィルタして使っています。

```json:都市の情報
  { city: 'Los Angeles',
   city_ascii: 'Los Angeles',
   lat: 33.98997825,
   lng: -118.1799805,
   pop: 8097410,
   country: 'United States of America',
   iso2: 'US',
   iso3: 'USA',
   province: 'California',
   state_ansi: 'CA',
   timezone: 'America/Los_Angeles' },
```

### 国旗
国旗は[ここ](https://www.countryflags.io/)のが、こんな感じでURLに `country code` 入れるだけで取れるので、使いやすかったです。

```html
<img src="https://www.countryflags.io/jp/flat/64.png">
```

### 背景画像
背景の都市画像は毎回違う画像が出てます。`Unsplash Source` という[素晴らしいサイト](https://source.unsplash.com/)を使っています。かなりいいクオリティーの画像を提供してくれる上に、ランダムとかキーワードに関連する画像だけといったフィルタまでできて、これスゲーわ。


## 都市を選択するUI
複数の都市を選択するUI部分ですが、入力が `tag` になる `multi-select` で決まりです。[ここ](https://vue-multiselect.js.org/)。選択したものが、`Vue` の `data` に入るので、もう、そのまま使ってます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32540/83dc72a4-637e-fd2e-cf3d-cddc93fd1c4b.png)


## 時差の計算

最初は上記の都市データから `lat`, `lng`を引数に Googleの `Time Zone API` で時差計算しようかと使おうかとしてましたが、[ここ](http://spacetime.how/)で `spacetime`というライブラリを発見。タイムゾーンをいい感じにいろいろといじることかできる、今回の用途にぴったりなライブラリを見つけてしまい、あっさり寝返りました。[これ](http://spacetime.how/)です。

```javascript:時差計算
<script src="https://unpkg.com/spacetime"></script>
<script>
  // make a new Date in New York
  var d = spacetime('March 1 2012', 'America/New_York')
  d = d.time('4:20pm')
  d = d.goto('America/Los_Angeles')
  d.time()
  //'1:20pm'
</script>
```
こんな感じで、複数の時間を扱うのが超簡単！ということで、もうバックエンドAPIいらん。全部フロントエンドでやることことに。

## タイムラインの配列つくって、ページネーション

### タイムラインをつくる
これは、`spacetime` に頼り切る。こんな感じで、ある `DateTime` をいろんな場所の時間に変換 （`goto`）できるんで、らくらく。作者に感謝。

```javascript:「NYの午後4時20分」は「LAの午後1時20分」
  var d = spacetime('March 1 2012', 'America/New_York')
  d = d.time('4:20pm')
  d = d.goto('America/Los_Angeles')
  d.time()  //'1:20pm'
```
あとは

```javascript
d = d.add(1, 'week')
d = d.add(30, 'minutes')
```
とかで時間を動かせるので、簡単にタイムラインのデータ（今回は配列）は作ることができる。

```javascript:その日のどのへんの時間かを0.0-1.0で表す
d.progress().day
```
`progress` といういけてるメソッドがあって、使って時間帯を0.0-1.0の間の値で返せる（素晴らしい！）。これで（朝・昼・夜）判定して、タイムラインに時間に応じた色つけをやったのだが...描画（処理）が極端に遅くなったために結局、使うの諦めた。

## ページネーション
右上の矢印のコントロールでタイムラインを「次の日」とか「次の週」とかにする方法。どうやってページネーションを簡単にできるかなーと調べていて、[このサイト](http://nekotheshadow.hatenablog.com/entry/2015/04/01/161343)見つけた。これで事前に配列の配列を作っておけばいいのではないか。

```javascript:配列をn個の要素づつの「配列の配列」にする
Array.prototype.divide = function(n) {
  var ary = this;
  var idx = 0;
  var results = [];
  var length = ary.length;

  while (idx + n < length){
    var result = ary.slice(idx,idx+n)
    results.push(result);
    idx = idx + n
  }

  var rest = ary.slice(idx,length+1)
  results.push(rest)
  return results;
}

// 使い方
['1am','2am','3am','4am','5am','6am','7am','8am','9am','10am'].divide(4) 
//=> [['1am','2am','3am','4am'],['5am','6am','7am','8am'],['9am','10am' ]]

['1am','2am','3am','4am','5am','6am','7am','8am','9am','10am'].divide(3) 
//=> [['1am','2am','3am'],['4am','5am','6am'],['7am','8am','9am'],['10am']]


```
これで、タイムライン全体（数日分）を1日分（24時間）ずつの時間の配列に分割すれば、簡単に次の日や次の週などに移動する（ページネーション）できる

## `Vue` のリアクティブ
今回ほとんどのデータがリアクティブになって欲しかった。`Vue` 勉強中の身としてはここはいろいろハマる。特にオブジェクトや配列の操作は普通に `push` とかじゃなくて、適宜 `splice()` や `$set` で操作しないとリアクティブにはならない。まあ、[公式読め](https://jp.vuejs.org/v2/guide/reactivity.html)という話。

## クリップボードにコピー
メールなどに貼り付けて送れるように、モーダルの画面で、ミーティング候補日をクリップボードにコピーするところ。これ、いろいろ試しました（いろいろ方法ある）が、最終的にいい感じに動いたのはこれ。`clipboard.js` [いいわ](https://clipboardjs.com/)。

```javascript:クリップボードにコピー

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.js"></script>

<div id="app">
    <textarea hidden v-model="jsonData" type="text"></textarea>
    <button class="btn" :data-clipboard-text="jsonData">Copy</button>
    <button class="btn" @click="add">Add</button>
</div>

<script>
var clipboard = new ClipboardJS('.btn');

clipboard.on('success', function(e) {
    console.info('Action:', e.action);
    console.info('Text:', e.text);
    console.info('Trigger:', e.trigger);
    e.clearSelection();
});

clipboard.on('error', function(e) {
    console.error('Action:', e.action);
    console.error('Trigger:', e.trigger);
});

var vm = new Vue({
    el: "#app",
    data: {
        jsonData: { id: 1, "name": "taro", "items": ["note", "pen", "eraser"] },
    }
})
```

## YAMLを扱う
クリップボードにコピーしたものはJSONだけど、YAMLの方が可読性がいいので変換する。

```javascript:JSONをYAMLに

<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.13.1/js-yaml.js"></script>

//  { id: 1, "name": "taro", "items": ["note", "pen", "eraser"] }
var json = JSON.stringify(this.jsonData)
var yaml = jsyaml.load(json)
this.yamlData = jsyaml.safeDump(yaml)
console.log(this.yamlData)
```

アウトプットこんな感じ。

```
id: 1
name: taro
items:
  - note
  - pen
  - eraser
```
うん。メール本文に貼り付けるなら、こっちの方がだいぶいい。

## 細かいUI周り
・タイムラインの為に、CCS `Bluma` で縦にコンテンツ配置
・`Bluma` の `HERO` の背景に画像を入れる

その他、細かいのはいろいろありますが、また時間のある時にでも書きたい。

## シリーズ

- [Vue.jsでSPA - [1] Element UIでベースの画面をつくる](https://qiita.com/narutaro/items/50d0e4714f93279502c9)
- [Vue.jsでSPA - [2] Element UIで各ペインの画面をつくる](https://qiita.com/narutaro/items/9abbf7881f692c985484)
- [Vue.jsでSPA - [3] vue-routerでルーティング ](https://qiita.com/narutaro/items/74178f4ea031b0ccfa44)
- [Vue.jsでSPA - [4] コンポーネントにしてみる ](https://qiita.com/narutaro/items/df793bb34129d113d9f2)
- [Vue.jsでSPA - [5] リアクティブになってる？](https://qiita.com/narutaro/items/bed4eab6e53e3b46c0b2)
- [Vue.jsでSPA - [6] サーバからのデータ取得](https://qiita.com/narutaro/items/82f4d9635c398524431c)
- [Vue.jsでSPA - [7] Vueからサーバデータ取得](https://qiita.com/narutaro/items/91da194788318c226e0e)
- [Vue.jsでSPA - [8] バックエンドとうまくやっていこうとして試したこと](https://qiita.com/narutaro/items/3b7135eed8f6e930a0e6)
- [Vue.jsでSPA - [9] 今更ながらCORSとそのエラーの回避方法](https://qiita.com/narutaro/items/26e4cbec386bc054bbd7)
- [Vue.jsでSPA - [10] Safari..お前か...3rd party cookie](https://qiita.com/narutaro/items/e53f653d73a6934046d0)
- [Vue.jsでSPA - [11] Element UIでログイン画面](https://qiita.com/narutaro/items/010009c0198371fee658)
- [Vue.jsでSPA - [12] ログイン：シングルペインからツーペインへ画面遷移](https://qiita.com/narutaro/items/fca953b3b2ed5c63360c)
- [Vue.jsでSPA - [13] モバイル向けに OnsenUI に手をだす](https://qiita.com/narutaro/items/bdc0e25c22369466bed3)
- [Vue.jsでSPA - [14] Vue.jsとOnsenUIを使ったオレオレなショッピングカートチュートリアル](https://qiita.com/narutaro/items/9502b90b565366911c57)
- [Vue.jsでSPA - [15] 世界中の人とミーティング時間を決める時に便利なやつ](https://qiita.com/narutaro/items/d6431f54050504bf1551)
- [Vue.jsでSPA - [16] へー、FirebaseでWebアプリのログインってこうやるのか](https://qiita.com/narutaro/items/3c37fe6c3b19a6143a07)


Cheers,