Vueで最初に作ったものがなぜかデジタル時計だったお話

Vueに触れ始めて少し経ったある日、

「Vueも少しずつ慣れてきたし、何か一つ自分で作ってみようかな。
でもそんなに難しいのはまだ無理だし…。
とりあえず**時計**作るか！！」

というわけでこんな感じのものができました。

![digital_clock.gif](https://qiita-image-store.s3.amazonaws.com/0/307269/2fbea70a-1ec4-7acc-e061-6c62df624047.gif)

少々ネタではありますが、学んだことを織り交ぜながら紹介していこうと思います。

# 構成図

![digital_clock_parts_discription.PNG](https://qiita-image-store.s3.amazonaws.com/0/307269/cd8e93f1-2a2e-c56e-1ea6-ecd71b2301e3.png)

図のようにコンポーネントを作って組み合わせていきます。

# LightStick 

オンで光ってオフで消える**「光る棒！！」**を作ります。

縦棒と横棒を選べるようにしておきます。

```vue:LightStick.vue
<template>
    <div class="stick" :class="[type, {light: light}]"  />
</template>

<script>
export default {
    props: {
        type: {
            typs: String,
            default: 'vertical'
        }
    },

    data() {
        return {
            light: false
        }
    },

    methods: {
        on() {
            this.light = true
        },

        off() {
            this.light = false
        }
    }
}
</script>

<style scoped>
.stick {
    background-color: rgba(200,255,200,0.2);
    border-radius: 10px;
}

.vertical {
    height: 50px;
    width: 10px;
}

.horizontal {
    height: 10px;
    width: 50px;
}

.light {
    background-color: yellowgreen;
}
</style>
```

# NumberPanel

光る棒を組み合わせて数字の形を作ります。

![NumberPanelDiscription.PNG](https://qiita-image-store.s3.amazonaws.com/0/307269/455be874-b79e-f7bc-1130-7484d409f7d0.png)

あとは数字に合わせて「光棒何号」を光らせるかを決めるだけです。
「1」なら「いけ！！3号！！6号！！」といった具合です。

```vue:NumberPanel.vue
<template>
    <div class="number-panel">
        <light-stick type="horizontal" class="stick stick1" ref="stick1" />
        <light-stick type="vertical"   class="stick stick2" ref="stick2" />
        <light-stick type="vertical"   class="stick stick3" ref="stick3" />
        <light-stick type="horizontal" class="stick stick4" ref="stick4" />
        <light-stick type="vertical"   class="stick stick5" ref="stick5" />
        <light-stick type="vertical"   class="stick stick6" ref="stick6" />
        <light-stick type="horizontal" class="stick stick7" ref="stick7" />
    </div>
</template>

<script>
import LightStick from "./LightStick"

export default {
    components: {
        LightStick
    },

    props: {
        number: 0
    }, 

    watch: {  
        number() {   //numberの変更とともに表示
            this.display()
        }
    },

    mounted() {   //初期表示設定
        this.display()
    },

    methods: {
        display() {
            switch(this.number) {
                case 0: 
                    this.zero()
                    break
                case 1:
                    this.one()
                    break
                case 2:
                    this.two()
                    break
                case 3:
                    this.three()
                    break
                case 4:
                    this.four()
                    break
                case 5:
                    this.five()
                    break
                case 6:
                    this.six()
                    break
                case 7:
                    this.seven()
                    break
                case 8:
                    this.eight()
                    break
                case 9:
                    this.nine()
                    break
            }
        },

        clear() {
            this.$refs.stick1.off()  //子コンポーネントのメソッド呼び出しにはref属性を使用
            this.$refs.stick2.off()
            this.$refs.stick3.off()
            this.$refs.stick4.off()
            this.$refs.stick5.off()
            this.$refs.stick6.off()
            this.$refs.stick7.off()
        },

        one() {
            this.clear()
            this.$refs.stick3.on()
            this.$refs.stick6.on()
        },

        two() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick3.on()
            this.$refs.stick4.on()
            this.$refs.stick5.on()
            this.$refs.stick7.on()
        },

        three() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick3.on()
            this.$refs.stick4.on()
            this.$refs.stick6.on()
            this.$refs.stick7.on()
        },

        four() {
            this.clear()
            this.$refs.stick2.on()
            this.$refs.stick3.on()
            this.$refs.stick4.on()
            this.$refs.stick6.on()
        },

        five() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick2.on()
            this.$refs.stick4.on()
            this.$refs.stick6.on()
            this.$refs.stick7.on()
        },

        six() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick2.on()
            this.$refs.stick4.on()
            this.$refs.stick5.on()
            this.$refs.stick6.on()
            this.$refs.stick7.on()
        },

        seven() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick3.on()
            this.$refs.stick6.on()
        },

        eight() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick2.on()
            this.$refs.stick3.on()
            this.$refs.stick4.on()
            this.$refs.stick5.on()
            this.$refs.stick6.on()
            this.$refs.stick7.on()
        },
        
        nine() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick2.on()
            this.$refs.stick3.on()
            this.$refs.stick4.on()
            this.$refs.stick6.on()
            this.$refs.stick7.on()
        },

        zero() {
            this.clear()
            this.$refs.stick1.on()
            this.$refs.stick2.on()
            this.$refs.stick3.on()
            this.$refs.stick5.on()
            this.$refs.stick6.on()
            this.$refs.stick7.on()
        }

    }
}
</script>

<style scoped>
.number-panel {
    background-color: #222;
    position: relative;
    height: 130px;
    width: 70px;
}

.stick {
    position: absolute;
}

.stick1 {
    top: 0;
    left: 10px;
}

.stick2 {
    top: 10px;
    left: 0;
}

.stick3 {
    top: 10px;
    left: 60px;
}

.stick4 {
    top: 60px;
    left: 10px;
}

.stick5 {
    top: 70px;
    left: 0;
}

.stick6 {
    top: 70px;
    left: 60px;
}

.stick7 {
    top: 120px;
    left: 10px;
}
</style>
```

# Separation

ただの区切りです。ハイ。

```vue:Separation.vue
<template>
    <div class="colon">
        <div class="circle" />
        <div class="circle" />
    </div>
</template>

<style scoped>
.circle {
    background-color: yellowgreen;
    border-radius: 5px;
    width: 10px;
    height: 10px;
}

.colon {
    height: 130px;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
}
</style>

```

# DigitalClock

NumberPanelとSeparationを並べて時計の形を作ります。

あとは時刻の各値をNumberPanelに割り当てればOKです。
時刻の取得については別途解説します。

```vue:DigitalClock.vue
<template>
    <div class="clock">
        <div class="display">
            <number-panel :number="hours2" />
            <number-panel :number="hours1" />
            <separation />
            <number-panel :number="minutes2" />
            <number-panel :number="minutes1" />
            <separation />
            <number-panel :number="seconds2" />
            <number-panel :number="seconds1" />
        </div>
    </div>
</template>

<script>
import NumberPanel from "./NumberPanel"
import Separation from "./Separation"
import moment from "moment"

export default {
    components: {
        NumberPanel,
        Separation
    },
    
    data() {
        return {
            time: undefined,
            intervalId: undefined
        }
    },

    methods: {
        setTime() {
            this.intervalId = setInterval(() => {
                this.time = new Date()
            }, 100)
        }
    },

    mounted() {
        this.setTime()
    },

    beforeDestroy() {
        clearInterval(this.intervalId)
    },

    computed: {
        hours1() {
            return moment(this.time).format("HH") % 10
        },

        hours2() {
            return Math.floor(moment(this.time).format("HH") / 10)
        },

        minutes1() {
            return moment(this.time).format("mm") % 10
        },

        minutes2() {
            return Math.floor(moment(this.time).format("mm") / 10)
        },

        seconds1() {
            return moment(this.time).format("ss") % 10
        },

        seconds2() {
            return Math.floor(moment(this.time).format("ss") / 10)
        }
    }
}
</script>

<style scoped>
.clock {
    background-color: #111;
    filter: drop-shadow(10px 10px 10px rgba(0,0,0,0.6));
    padding: 50px;
}

.display {
    background-color: #222;
    display: flex;
    justify-content: space-between;
    padding: 10px;
    width: 600px;
}    
</style>
```

## setInterval

時計を実装するために一定間隔で時刻を取得する必要があります。
その時に使用するのが**setInterval**です。

```javascript
data() {
    return {
        intervalId: undefined    //1. clearIntervalのためのIDを保持します
    }
},

methods: {
    do() {    //2. 一定間隔で処理を実行するためメソッドを用意します
        this.intervalId = setInterval(() => {  
            //処理内容
        }, 1000) //1秒間隔で処理
    }
},

mounted() {  //3. 上記で用意したメソッドをマウントのタイミングで呼び出します。
             //   これによりこのコンポーネントは一定間隔で処理を実行することになります。
    this.do()
},

beforeDestroy() {    //4. 使用後はしっかりとクリアする必要があります
    clearInterval(this.intervalId)
}
```

## Moment.js

- [Moment.js](https://momentjs.com/)

```shell
$npm install moment
```

javascriptで日付処理を容易にするためのライブラリです。
今回はフォーマットで使用しています。
これ以上はすみませんが割愛します。

[vue-moment](https://www.npmjs.com/package/vue-moment)というのもあるみたいです。
詳しくは見ていません。ハイ。

# おわりに

フロント学び始めたばかりだけど自由度が高くて楽しい！！でも難しい！！

今回のももっといい方法があるかもしれませんが、**棒を光らせたい！！**という思いでこんな感じになってしまいました。

次はもっといいものを作りたいですね。