スクリーンショット
なにはともあれスクリーンショット
タイトルの通り、時差を確認しながらミーティング時間の候補を選ぶことのできるSPAを作ってみたので、その時に調べたり、考えたりしたことを書いてみることに。
各種ライブラリに頼って簡単に作るつもりでしたが、こんなシンプルなものでも動くものを書くにはそれなりにやることあるねぇ... という感じでした。勉強になった。
デモ
- 都市をいくつか選んで
- でてくるタイムラインからいくつか時間を選んで
- 右下のボタン押したら、ミーティング候補を見れて、コピペもできます
ライブデモ
ライブデモはこちら
やったことをつらつらと
今回調べたり、ハマったりした出来上がるまでの過程をつらつらと書いていきます。
国や都市の情報
都市データ
国名・都市名・規模(人口)の情報は、ここの情報をつかわせていただきました。人口○万人以上みたいな感じで主要な都市だけをフィルタして使っています。
{ 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' },
国旗
国旗はここのが、こんな感じでURLに country code
入れるだけで取れるので、使いやすかったです。
<img src="https://www.countryflags.io/jp/flat/64.png">
背景画像
背景の都市画像は毎回違う画像が出てます。Unsplash Source
という素晴らしいサイトを使っています。かなりいいクオリティーの画像を提供してくれる上に、ランダムとかキーワードに関連する画像だけといったフィルタまでできて、これスゲーわ。
都市を選択するUI
複数の都市を選択するUI部分ですが、入力が tag
になる multi-select
で決まりです。ここ。選択したものが、Vue
の data
に入るので、もう、そのまま使ってます。
時差の計算
最初は上記の都市データから lat
, lng
を引数に Googleの Time Zone API
で時差計算しようかと使おうかとしてましたが、ここで spacetime
というライブラリを発見。タイムゾーンをいい感じにいろいろといじることかできる、今回の用途にぴったりなライブラリを見つけてしまい、あっさり寝返りました。これです。
<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
)できるんで、らくらく。作者に感謝。
var d = spacetime('March 1 2012', 'America/New_York')
d = d.time('4:20pm')
d = d.goto('America/Los_Angeles')
d.time() //'1:20pm'
あとは
d = d.add(1, 'week')
d = d.add(30, 'minutes')
とかで時間を動かせるので、簡単にタイムラインのデータ(今回は配列)は作ることができる。
d.progress().day
progress
といういけてるメソッドがあって、使って時間帯を0.0-1.0の間の値で返せる(素晴らしい!)。これで(朝・昼・夜)判定して、タイムラインに時間に応じた色つけをやったのだが...描画(処理)が極端に遅くなったために結局、使うの諦めた。
ページネーション
右上の矢印のコントロールでタイムラインを「次の日」とか「次の週」とかにする方法。どうやってページネーションを簡単にできるかなーと調べていて、このサイト見つけた。これで事前に配列の配列を作っておけばいいのではないか。
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
で操作しないとリアクティブにはならない。まあ、公式読めという話。
クリップボードにコピー
メールなどに貼り付けて送れるように、モーダルの画面で、ミーティング候補日をクリップボードにコピーするところ。これ、いろいろ試しました(いろいろ方法ある)が、最終的にいい感じに動いたのはこれ。clipboard.js
いいわ。
<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の方が可読性がいいので変換する。
<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でベースの画面をつくる
- Vue.jsでSPA - [2] Element UIで各ペインの画面をつくる
- Vue.jsでSPA - [3] vue-routerでルーティング
- Vue.jsでSPA - [4] コンポーネントにしてみる
- Vue.jsでSPA - [5] リアクティブになってる?
- Vue.jsでSPA - [6] サーバからのデータ取得
- Vue.jsでSPA - [7] Vueからサーバデータ取得
- Vue.jsでSPA - [8] バックエンドとうまくやっていこうとして試したこと
- Vue.jsでSPA - [9] 今更ながらCORSとそのエラーの回避方法
- Vue.jsでSPA - [10] Safari..お前か...3rd party cookie
- Vue.jsでSPA - [11] Element UIでログイン画面
- Vue.jsでSPA - [12] ログイン:シングルペインからツーペインへ画面遷移
- Vue.jsでSPA - [13] モバイル向けに OnsenUI に手をだす
- Vue.jsでSPA - [14] Vue.jsとOnsenUIを使ったオレオレなショッピングカートチュートリアル
- Vue.jsでSPA - [15] 世界中の人とミーティング時間を決める時に便利なやつ
- Vue.jsでSPA - [16] へー、FirebaseでWebアプリのログインってこうやるのか
Cheers,