Vue.jsとCSSアニメーションでラーメンタイマーを作る + Firebaseで簡単公開

学びたてだけどVue.js + CSSアニメーション + 絵を組み合わせて作ってみたい！
という事でシンプルなカップラーメンのタイマーを作ってみました。

基本的なVue.jsで始めたばかりの方でも比較的読み進めやすいと思います。
話題のFirebaseで無料公開もとても簡単だったので方法をまとめました。

# 🍜 ラーメンタイマー

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/8e99e515-a30d-9d85-8bde-c2c1b74d3c0a.gif" width=85%>

**・アプリURL
https://ramen-timer.com/
・GitHub - ソースコード
https://github.com/aocattleya/Ramen-Timer**
（いいねやスター貰えるととても喜びます！）
 　 

## アプリの内容

| 使用している物 | 説明 |
| :--: | :--: |
|HTML, CSS|animationプロパティを使用|
|Vue.js|JavaScriptフレームワーク|
|Firebase|無料枠でアプリを公開する|
|---|---|
|SweetAlert|アラートを簡単にデザインするライブラリ|
|GoogleFonts|フォントの変更|
|FireAlpaca|ペイントツール（無料）|

　
3分 or 5分のボタンをホバーするとキャラクターが左右に振り向きます。
時間を選びクリックするとカウントダウンタイマーが使える簡単な内容です。
レスポンシブデザイン（スマホ）にも対応しています。※左右の振り向きはしない
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/e85504e8-b215-5d5c-bfa9-0719c8df9eef.jpeg" width=40%>

## キャラクターデザイン

まず一番目立つキャラクター
自分は元々漫画家のアシスタントをしていた時期などがあり、
描いた物をアプリに組み合わせてみたかったので描き描き。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/3f1749a4-dc9d-d0d0-7b00-a8a00ba35ead.png" width=80%>

レイヤーといって何枚もの板が重なっているように描かれています。
なので例えば手を消すと下に隠れている髪が出てきます。
これによって一枚描いたら背景などが簡単に変更でき、左右反転させて手を描けば2枚完成。

※猫の方が万人受けする。名前募集中！

**こちらに原寸サイズ画像＆レイヤーファイルを公開しています。**
**[illustration-Original_Characters](https://github.com/aocattleya/illustration-Original_Characters)（GitHub）**
　

# 🔍 コードの解説

それぞれ分けて解説していきます。
コードは分かりやすいように関係ない部分を大幅に省略して書いてます。
 
　

## ・キャラクターを左右に振り向かせる

1行目、`v-bind:class=""`によってclassを「右顔CSS」⇔「左顔CSS」と書き替えており、
きっかけは、それぞれの3分と5分ボタンにある`v-on:mouseover=""`

```html:index.html
<!-- キャラクター -->
<img v-bind:class="{ right_face: isRight, left_face: isLeft }" />
  <div class="pick-button">
    <!-- 3分ボタン -->
    <input type="image" src="img/3min.png" v-on:mouseover="rotate_left" />
    <!-- 5分ボタン -->
    <input type="image" src="img/5min.png" v-on:mouseover="rotate_right" />
  </div>
</div>
```

　
JavaScript側では下記のようにボタンをホバーした時に、
「true」⇔「false」で切り替わる処理を書いています。


```js:main.js
const vm = new Vue({
  el: "#app",
  data: {
    isLeft: false,
    isRight: true,
  },
  methods: {
    rotate_left: function() {
      this.isLeft = true;
      this.isRight = false;
    },
    rotate_right: function() {
      this.isLeft = false;
      this.isRight = true;
    }
  }
});
```

　
右顔のCSSアニメーション

```css:style.css
.right_face {
  border-radius: 50%;
  background-image: url(../img/button_right.png);
  animation: anime 0.4s linear 2;
}
@keyframes anime {
  100% {
    transform: rotateY(360deg);
  }
}
```

・`border-radius: 50%`で画像を丸くする。
・そして`animation`プロパティ、長さなどを設定でき`@keyframes`で動きを付けます。
　上記は`100%`終了時までに`Y軸に360度回転`としています。
　
　
　

## ・ボタン選択で画面の切り替え

ページは変わっていません。
Vue.jsで最初の画面を非表示にし、非表示になっていたタイマー画面を表示させています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/8c507bc2-9f94-a183-1f9f-76aed66b994a.gif" width=80%>

3分 or 5分ボタンを押すと`v-on:click="show = !show"`によって、
`<div v-show="!show">` ⇔ `<div v-show="show">`と画面表示が切り替わります。

```html:index.html

<!-- 3分 or 5分ボタン選択画面 -->
<div v-show="!show">
  <input type="image" src="img/3min.png" v-on:click="show = !show" />
  <input type="image" src="img/5min.png" v-on:click="show = !show" />
</div>

<!-- タイマー画面 -->
<div v-show="show">
  <span id="minutes">{{ minutes }}</span>
  <span id="middle">:</span>
  <span id="seconds">{{ seconds }}</span>
  <!-- 省略 -->
</div>
```
　
　

## ・タイマーの実装

![timre3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/8801b262-ffdb-4222-16bb-4d711581d3db.gif)



```html:index.html
<!-- 3分 or 5分ボタン -->
<input type="image" src="img/3min.png" v-on:click="show = !show; threeMin()" />
<input type="image" src="img/5min.png" v-on:click="show = !show; fiveMin()" />
```

3分 or 5分ボタンをクリックした時に、
`v-on:click="threeMin()"`又は`v-on:click="fiveMin()"`が実行され、
タイマー画面でカウントする時間を3:00 or 5:00にします。
　

```html:index.html
<!-- タイマー -->
<div class="timer">
  <span id="minutes">{{ minutes }}</span>
  <span id="middle">:</span>
  <span id="seconds">{{ seconds }}</span>
</div>
<div id="buttons">
  <!-- スタート -->
  <input type="image" src="img/start.png" v-on:click="startTimer" v-if="!timer" />
  <!-- ストップ -->
  <input type="image" src="img/stop.png" v-on:click="stopTimer" v-if="timer" />
  <!-- リセット -->
  <input type="image" src="img/reset.png" v-on:click="resetTimer" v-if="resetButton" />
</div>
```

タイマーのカウントは、HTMLの`{{ minutes }} : {{ seconds }}`
この部分がVue.jsの処理でタイマーのように変更されていきます。
　

```js:main.js
const vm = new Vue({
  el: "#app",
  data: {
    timer: null,
    pickTime: null,
    totalTime: null,
    resetButton: false
  },
  methods: {
    // 3分 or 5分ボタン
    threeMin: function() {
      this.pickTime = 3 * 60;
      this.totalTime = this.pickTime;
    },
    fiveMin: function() {
      this.pickTime = 5 * 60;
      this.totalTime = this.pickTime;
    },
    // スタート
    startTimer: function() {
      this.timer = setInterval(() => this.countdown(), 1000);
      this.resetButton = true;
    },
    // ストップ
    stopTimer: function() {
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = true;
    },
    // リセット
    resetTimer: function() {
      this.totalTime = this.pickTime;
      clearInterval(this.timer);
      this.timer = null;
      this.resetButton = false;
    },
    // 秒が一桁の場合0を追加
    padTime: function(time) {
      return (time < 10 ? "0" : "") + time;
    },
    // カウントダウン
    countdown: function() {
      if (this.totalTime >= 1) {
        this.totalTime--;
      } else {
        this.totalTime = 0;
        this.resetTimer();
        swal("Complete!!", "", "success");
      }
    }
  },
  computed: {
    // タイマーの数値
    minutes: function() {
      const minutes = Math.floor(this.totalTime / 60);
      return minutes;
    },
    seconds: function() {
      const seconds = this.totalTime - this.minutes * 60;
      return this.padTime(seconds);
    }
  }
});
```

`computed:`の部分がHTMLのタイマー`{{  }}`に表示され、ボタンで各`methods:`が働き、
スタートを押すと`setInterval()`により、一秒ごとに`countdown`が実行されます。
　
　
　

## ・アラートデザイン『SweetAlert』

![sweet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/5bef5638-314a-41fa-3259-87a10cfa59d5.png)

簡単にアラートがデザイン出来るライブラリ
ブラウザからCDN経由で読み込みができます。

```html:index.html
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
```

```js:main.js
swal("Complete!!", "", "success");
```

これだけ、凄い！
このアラートはスマホでも同じデザインで表示されます。

その他の使い方は下記のページが分かりやすいです。
- [SweetAlertバージョン2を使ったデモ](http://d.bsnws.net/p/sweetalert%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%83%87%E3%83%A2/)
　
　
　
# 🌐 Firebaseで公開（無料）

[**Firebase - Google**](https://firebase.google.com/?hl=ja)
　
最近とても話題のFirebaseで、作成したアプリを無料で公開。
データベースなど複雑に使う事も可能ですが、今回は簡単に無料で公開してみます。

下記はQiita用に`test`プロジェクトで進めていますが、任意の名前にしてください。

**１、ログイン**
Firebaseのページを開いてログイン

**２、プロジェクトの作成**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/eef78422-aa81-14fc-47f6-ab02e83126b8.png" width=45%>
プロジェクト名を記入します。
IDの部分はURLになりますので好みのIDに書き変えましょう。

Googleアナリティクスの設定を聞かれます。
今回は、使用しないで進めます。

**３、フォルダの設定**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/98b897d9-1f90-4739-ee6f-661fe66d30a2.png" width=40%>
Desktopにプロジェクト名と同じフォルダを作成します。
中にpublicという名前でフォルダを作成して公開したいコードを入れます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/784c1575-1084-e50d-dd4e-0ed1ff415aff.png" width=70%>


**４、Node.jsのインストール**
まだ入ってない方はインストールしてください。
すぐ終わります。

[**Node.js**](https://nodejs.org/ja/)

**５、firebase-toolsをインストール**

```
npm install -g firebase-tools
```

**６、ログイン**

```
firebase login
```
利用状況のデータを送っても良いか聞かれたら任意でY/n
次にGoogleログイン画面が表示されるので、アカウントを選び進めます。

**７、デプロイ設定**

```
cd Desktop/test
```

プロジェクトのフォルダに移動します。

```
firebase init
```
下記のような画面が表示されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/2ec2de8d-c7f5-1d9b-10a1-053480357b10.png" width=50%>

```
◉ Hosting: Configure and deploy Firebase Hosting sites
```
`Hosting`をスペースで選択してからEnterを押します。
※スペースで選択していなとエラーとなる。

```
? Please select an option: 
```
`Don't set up a default project `を選択してEnter

```
? What do you want to use as your public directory?(public)
```
そのままEnter（デフォルトの`public`となる）

```
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) 
```
そのままEnter（デフォルトでN）

```
? File public/index.html already exists. Overwrite? (y/N) 
```
index.htmlを上書きしていいか聞いてくるのでN（そのままEnterでN）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/64febe99-2422-17c5-4b23-e3f042d45748.png" width=50%>
上手くいきました。

**８、公開する**

```
firebase use --add
```
上記でEnter

```
? Which project do you want to add?
> test-71a0e
```
作成したプロジェクトを選択します。

```
? What alias do you want to use for this project? (e.g. staging)
```
名前は任意です。今回は`staging`としました。


```
firebase deploy
```
完了です！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/98c8b95d-bd12-7c42-09ba-4c67d22d6904.png" width=50%>
内容を変更した場合は、再度`firebase deploy`すればOKです。

**９、`Hosting URL:`をブラウザでアクセス**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/8ba4680a-45d7-2033-65b3-e3248b430ed0.png" width=50%>

こうして完成、簡単にFirebaseでの公開も出来ました。**使ってね！**

**・ラーメンタイマー
https://ramen-timer.com/
・ソースコード - GitHub
https://github.com/aocattleya/Ramen-Timer**
　
　

## 終わりに

Vue.jsで何か作ってみたい、CSSアニメーションと絵も組み合わせたい！

そんな考えから作ってみたアプリでシンプルに見えて苦戦もありました。
しかし言語を学習してるだけと違い、新たな発見と理解はとても多かったです。
基本的な構文でここまで作れて達成感も大きかったのでやって凄く良かったと思います。

次はさらにレベルを上げて作ってみたいですね！
最後まで読んでくれてありがとうございました。
　
## 追記（PWA対応）
PWA（Progressive Web Apps）に対応しました。
スマホでアクセスしてホーム画面に追加するとオフラインで使えるタイマーアプリになります。
Macでもオフラインアプリとしてダウンロード出来ます。
![eFJeHTAg.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/77e7b88b-6870-0fd2-fbc7-7bd526dfd221.jpeg)


## 参考

・[【CSS3】@keyframes と animation 関連のまとめ](https://qiita.com/7968/items/1d999354e00db53bcbd8)
・[SweetAlertバージョン2を使ったデモ](http://d.bsnws.net/p/sweetalert%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%83%87%E3%83%A2/)
・[Firebaseを用いて5分でセキュアなWebサイトを公開する](https://qiita.com/potato4d/items/95eaf2e41404711e621e)

## リンク

:octocat: GitHub
https://github.com/aocattleya
🐦 Twitter
https://twitter.com/aocattleya
📗 Qiita 
https://qiita.com/aocattleya