2018-10-31に更新

GAE+Nuxt.js+Firebaseでネココさんを喋らせるだけのサービス作成

昔mixiアプリでネココレースというアプリを作ったのですが、その素材が放置状態でもったいない事に気づいたので、ネココさんを着替えさせて喋らせてTwitter上に投稿できる簡易なWebアプリを作ってみました。

yf4qcSgR8rZR0fmmSwS7.png

ネココさんトーク

ログインなど不要ですぐ使えます。

構成

Google App Engine

この手のWebサービスにはあまり意味がないのですが、SSR(サーバーサイドレンダリング)で一応全部ページクロールできるようにして多少SEO的な効果も期待し、静的ホスティングサービスではなくGoogle App Engineにしてみました。

Nuxt.js

最近は簡単なアプリケーションはNuxt.jsだととにかく簡単に色々作れるのでよく使っています。前述と同様、SSR目的でもあります。

Firebase

今回はデータを登録して表示するだけというシンプルなデータ構成のため、わざわざサーバーを用意しなくてもよいFirebaseを使ったサーバーレス構成にしました。

データの保存にFirestore、画像の保存にStorageを使っています。

Buefy

Bulmaというやわらかい感じのデザインを実装できるフレームワークを使っています。ただ、Bulma自体はほんとにCSSだけで、動きなどは自分で付けなければなりませんので、動き部分も実装されているBuefyを使っています。これを入れるだけでBulma自体のCSSもそのまま使うことができ汎用的で非常に便利です。

仕組み

プロジェクトの作成

プロジェクトはcreate-nuxt-appを使って作成し、TypeScriptを追加しました。以前下記でまとめていた方法になります。

Nuxt.js+ExpressのプロジェクトをTypeScript化する

Storeの利用

動物や着せ替えの選択は、選択したコンポーネントだけでなく、画像のダウンロードやシェアする画像を作成する等、他の場所でも使うためにStoreのStateとして保持しておきます。

StoreはNuxt.jsのマニュアルを見ると非常に理解しやすく、下記の記事でも以前まとめています。

VuexのStoreはNuxt.jsのマニュアルを見るとすぐ理解できる

画像の作成

画像は html2canvas で作成しました。今回はサーバーが無いためこれを使ってブラウザ上で画像を作成する形です。left: -2000px とかにしたdiv上にあれこれ配置し、それをhtml2canvasで変換しているだけです。

一点注意点として、デフォルトの挙動では端末のサイズによってcanvasのスケールが変わってしまうようですので、下記のように等倍で作成します。

    const canvas = await html2canvas(this.$refs.canvas, { scale: 1 })

Blobに変換する方法は下記等、色々情報があります。

Canvasで描画した画像を送信してサーバに保存する - Qiita

canvas.toBlob もあるみたいですね。早く全てのブラウザで実装されてほしいものです。

あとはStorageへの登録はマニュアル通りです。ファイル名は登録したメッセージデータのID名にしています。

    const ref = firebase.storage().ref()
    const imageRef = ref.child(`images/${messageId}.png`)
    const imageSnapshot = await imageRef.put(blob)

画像のURLも取っておきます。調べた情報によると、上記のimageRefでなく、再度Storage側から取ってきたものを使わないとダメなようです。

    const ref = firebase.storage().ref()
    const imageRef = ref.child(`images/${messageId}.png`)
    const url = imageRef.getDownloadURL()

このURLもメッセージデータに保存しておけば完成です。

あとは表示画面で入力されたメッセージや画像のURLをメタタグに入れておけば、その詳細ページのURLをシェアしてもらうことでツイートでシェアすることができます。

Nuxt.jsの場合メタタグの設定はページコンポーネント内で下記のように行います。

  head() {
    const title = this.getTitle() + ' - ネココさんトーク'
    const description = `${this.categoryExplanation}:${this.message.message}`
    return {
      title,
      meta: [
        {
          name: 'description',
          content: description
        },
        { name: 'og:title', content: title },
        {
          property: 'og:image',
          content: this.message.imageUrl
        },
        {
          property: 'og:description',
          content: description
        },
        {
          name: 'twitter:card',
          content: 'summary_large_image'
        },
        {
          name: 'twitter:image',
          content: this.message.imageUrl
        }
      ]
    }
  }

SSRのためデータはasyncDataで取っておきましょう。

  async asyncData({ params }) {
    const messageRef = messagesCollection.doc(params.id)
    const message = await messageRef.get()
    const messageData: any = message.data()
    messageData.id = message.id
    return {
      message: messageData
    }
  }

シェアする画面へのURLの作成は下記のような感じです。

  tweetUrl() {
    const url = encodeURIComponent(
      process.env.URL + `/messages/${this.message.id}`
    )
    const text = encodeURIComponent(this.categoryExplanation)
    const hashtags = encodeURIComponent('ネココさんトーク')
    return `https://twitter.com/share?url=${url}&text=${text}&hashtags=${hashtags}`
  }

デプロイ

Google App Engineへのデプロイは下記で以前解説しています。

App Engineの標準環境でNuxtを使って無料SSR

ZeitのNowでも良いと思います。GAEはFirebaseのプランが変わってしまう(無料枠は利用可能です)、Zeitは無料でのデプロイ回数制限がある、という違いがあるので適宜良い方を選択してください。

おまけでGA

Google Analyticsの導入は下記をインストールして設定するだけでした。めちゃくちゃ簡単。

nuxt-community/analytics-module: Google Analytics Module

まとめ

大雑把になりますが作り方をざっと紹介しました。興味のある方は是非試してなにか作ってみてください!

不明点があればTwitter等で聞いてもらえれば可能な範囲で答えます。


dala00

Crieitの開発者です。 主にLAMPで開発しているWebエンジニアです(在宅)。大体10年程。 記事でわかりにくいところがあればDMで質問していただくか、案件発注してください。 業務依頼、同業種の方からのコンタクトなどお気軽にご連絡ください。 業務経験有:PHP, MySQL, Laravel5, CakePHP3, JavaScript, RoR 趣味:Elixir, Phoenix, Node, Nuxt, Express, Vue等色々

Crieitはαバージョンで開発中です。進捗は公式Twitterアカウントをフォローして確認してください。 興味がある方は是非記事の投稿もお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか
関連記事

コメント