人生を要約するサービス  "Digest-of-Life" -５秒でエモい画像を作れるようにするProject-

# お詫び
アドベントカレンダーの記事と同時にリリースする予定で作成していましたが、
バックエンドで使用するAPIの`production Licence`が間に合わず、リリースできませんでした...
来月くらいにリリースする予定ですので、もしご興味を持っていただけたならば、ストックをお願いします。
（記事の更新で通知させていただきます。:bow_tone1:）


# 作ったもの（というか作っているもの）

![d-o-life6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/10cd089c-72f6-1625-e483-b8a6d616ee96.gif)

"Digest-of-Life"

人生を要約するサービスを作ってます。
提供するものは、**大切にしたい言葉を、きれいな画像とともに保存するサービス**です。
`こっそり記録していってもいいし、その場で画像に合成して共有してもいい`

文字を画像にしたいとき、きれいな画像で言葉をドーンと伝えたいとき等にご利用ください。

# 動機（なぜ作ろうと思ったか）
![digest-of-life.003.jpeg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/9a5827e5-14fe-12c0-0852-64455a277a4d.jpeg)

**伊坂幸太郎著「モダンタイムス」**の作中のセリフですが、昔読んでからずーっと頭の片隅に残ってまして、、、
でも、個人的には**哲学は人生の要約**だと信じているので、それを形にできたらなと思い、
今回、個人の哲学を記録するサービスを作るに至りました。

# アプリケーション構成
![digest-of-life.008.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/25ea9b9e-3eda-44e3-a1e2-239302994bef.jpeg)


ほぼすべてFirebaseで作成しました。
`API`も`データベース`も`ログイン機能`も`javascript`ちょこっと書くだけで実装できる`Firebase`先生素晴らしい！


## ホスティングサービス： `Firebase Hosting`
- これがないとWebサービスが動かない。
- ローカルのエミュレータで動作確認したあと、コマンド１つでデプロイできる。

## API： `Firebase Functions`
- 面倒なサーバーサイドのAPIを`javascript`で数行書いてデプロイするだけで動く。
- 今回は、`Unsplush api`の`api key`を秘匿するために使用。

## データベース： `Firebase Realtime Database`
- メソッド１つ書くだけで、clientからDBにCRUDできる。すごい。。。
- 今回は、ユーザーが保存した言葉や写真のデータを永続化するために使用。

## ユーザー管理： `Google アカウント`
- `Firebase`側に`Google ログインをアプリに統合`する機能が用意されていたので。
- ユーザーを管理せずに、ユーザーを識別できるって素晴らしい。

## 画像サービス： `Unsplush api`
- **今回の目玉**、めちゃきれいな画像が大量にあるサイトのAPI
    - [Unsplush](https://unsplash.com/)
    - [Unsplush developers](https://unsplash.com/developers)
- APIを利用すると、細かい検索機能が使える。
- 普通の`developアカウント`だと`50requests/hour`、ガイドラインに沿ってることを示せれば、`Production版`の`api key`がもらえる

![Unsplash_CaseStudy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/6b9918a2-e756-6bd8-9dbb-4483c6d0edd4.png)

# 要素技術
使用した技術要素と検証に使ったサンプルコードを書きます。

## `vuetify` なるべくCSS書かないマン
![20180730160701.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/4192ecde-e835-bc55-b5f7-f57e714202fc.jpeg)
- https://vuetifyjs.com/ja/
- `<v-XXXX>`のような独自のコンポーネントを使用して、独自のクラスで`flex` `margin` `padding`等を適用します。
- 今回の見た目はほぼ`vuetify`のコンポーネントの見た目そのままになってます。
- `モーダル`や`ページング`、`トースト`等がすごく楽になります。

## `html2canvas` Webページの一部を画像にする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/36de4a4d-b588-cfca-8b5e-489ee861d2bb.png)
- https://html2canvas.hertzen.com/
- 「Webページをレンダリングして`Canvas`上に描画する」処理を行います。

- 2種類のやり方のうち後者を採用しています。

①読み込んだ要素を`toDataURL`にして`<img>`の`src`に埋め込むタイプ
(こちらの記事より: [[JavaScript] JSだけでスクリーンショットを撮ってダウンロードもする方法(Qiita)](https://qiita.com/youwht/items/8b681a856f59aa82d671))

```html:html
<div id="target">
<!-- ここがキャプチャされる -->
</div>
<!-- 下に生成した画像が出る -->
<img src="" id="result" />
```

```javascript:javascript

html2canvas(document.getElementById("target"),{
  onrendered: function(canvas){
  //imgタグのsrcの中に、html2canvasがレンダリングした画像を指定する。
  var imgData = canvas.toDataURL();
  document.getElementById("result").src = imgData;
 }
});
```

②読み込んだ要素を指定した場所の子要素に`canvas`を挿入する

```html:html
<div id="target">
<!-- ここがキャプチャされる -->
</div>

<div id="result">
<!-- ここに生成した画像が出る -->
</div>
```

```javascript:javascript
html2canvas(document.querySelector("#target"))
  .then(function(canvas) {
    var result = document.querySelector("#result");
    result.innerHTML = "";
    result.appendChild(canvas);
}
```



# ハマったところ
## `vuetify`の`<v-text-field>`で`vue.js`の`v-model.lazy` が使えない
> イベントリスナーを使用して手動で行う必要があります
    - https://github.com/vuetifyjs/vuetify/issues/1810

- 使えないようです。「入力が完了したら自動で検索」を実装したかったのですが、できませんでした。
- エンターキーで検索に切り替えました。

```vuejs:tmp.vue
<v-text-field v-model="serchWords" @keydown.enter="execSearch(serchWords)"/>
```

## `html2canvas`で、外部から読み込んだ画像が表示されない
デフォルトだと`CORS`で引っかかって、描画できないみたいです。
> attribute: allowTaint
> type: boolean
> default: false
> description: Whether to allow cross-origin images to taint the canvas

- こちらを参考に、`option`を追加したらうまくいきました。
    - [html2canvas does not render images located in the element](https://github.com/niklasvh/html2canvas/issues/722)

```diff:javascript
tml2canvas(document.querySelector("#target"), {
+        letterRendering: 0,
+        useCORS: true,
+        allowTaint: true
      }).then(function(canvas) {
        var result = document.querySelector("#result");
        result.innerHTML = "";
        result.appendChild(canvas);
      });
```


### `Firebase Funtions`で外部通信ができない
```
Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions
```
- 無料枠(Sparkプラン)だとダメみたいです。
- Flameプラン($25/月)に切り替えて解決しました。
 - 参考：[Cloud Functions for Firebaseに関する雑記（Qiita）](https://qiita.com/Miracle-T-Shirt09/items/3c17b1d4b7ee00166f41)

## `firebase emulators`のAPIの向き先がクラウドになる。
```bash
$ firebase emulators:start
```
上記コマンドで `Hosting`と`Functions`を起動した状態で、`Hosting上のvue.js`から`API(Functions)`を叩くと、クラウドの`Functions`を見に行ってしまい、`CROS`エラーになる。（404とかではなくて）

- `emulator`では`Hosting`だけを上げて、`Functions`はデプロイしてテストするようにしました。(`functions.https.onCall()`の方を使ってます。)
- 見逃してるだけできっと設定があると思います。

# 今後の展望
- `Unsplush`から`Production api key`がいただけたらサービスとしてリリースしようと思います。（１月予定）
- 欲しい機能とかあれば気軽にコメントください！！喜びます:blush:

```tex:v1.0予定機能 
- googleでログインでき、「個人の言葉」を美しい画像ともに保存できる
- 画像と言葉の組み合わせで、「５秒でエモい画像が作れる」
- 画像のランダム生成で大喜利
- Twitterでの共有
```

# 最後に
ここまで、お読みいいただきありがとうございます！
最後に、開発途中で特に意味もなく作った画像を添えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/15e3c950-d568-a142-a81d-1fbd9d12cf7a.png)



# おまけ：宣伝用Key Note
![digest-of-life.001.jpeg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/d097b0de-2953-b541-67f9-10f5516e6cf3.jpeg)
![digest-of-life.002.jpeg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/1ac4712e-2415-d1cd-64d7-f0441ad90686.jpeg)
![digest-of-life.003.jpeg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/4a144bf4-0c85-da49-361f-ebf5e9f57f22.jpeg)
![digest-of-life.004.jpeg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/465f6412-6707-f8cd-b22d-805b37193490.jpeg)
![digest-of-life.005.jpeg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/67d2fd72-6aab-8891-f90d-031380cd7dd2.jpeg)
![digest-of-life.006.jpeg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172701/198a76e9-d725-070b-5123-50a0ff62df8a.jpeg)