TwitterやFacebookなどで見かける「投稿してから○分経過」のようなコンポーネントを作成しました。
setInterval()
を使用し、vue.jsとTypescriptで実装しています。表示する時間の単位によってインターバルの間隔を変更しています。ほぼTypescriptなので、他のjavascriptフレームワークにも簡単に移植出来ると思います。
全体像
src/components/LastTime.vue
<template>
<div>{{ lastTime }}</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
enum IntervalTime {
SEC = 1000,
MIN = 60 * IntervalTime.SEC,
HOUR = 60 * IntervalTime.MIN,
DAY = 24 * IntervalTime.HOUR,
WEEK = 7 * IntervalTime.DAY,
YEAR = 52 * IntervalTime.WEEK,
}
enum TimeUnit {
SEC = 'sec',
MIN = 'min',
HOUR = 'hour',
DAY = 'day',
WEEK = 'week',
YEAR = 'year',
}
@Component
export default class LastTime extends Vue {
@Prop({ default: () => 0 }) baseTime!: number
duration: number = 0
intervalId: number = 0
intervalTime: IntervalTime = IntervalTime.SEC
timeUnit: TimeUnit = TimeUnit.SEC
created() {
const currentTime = new Date().getTime()
this.duration = currentTime - this.baseTime
this.intervalId = setInterval(() => {
this.timer()
}, this.intervalTime)
}
timer() {
const currentTime = new Date().getTime()
this.duration = currentTime - this.baseTime
if (this.duration > IntervalTime.YEAR) {
this.restartInterval(IntervalTime.YEAR, TimeUnit.YEAR)
} else if (this.duration > IntervalTime.WEEK && this.intervalTime <= IntervalTime.DAY) {
this.restartInterval(IntervalTime.WEEK, TimeUnit.WEEK)
} else if (this.duration > IntervalTime.DAY && this.intervalTime <= IntervalTime.HOUR) {
this.restartInterval(IntervalTime.DAY, TimeUnit.DAY)
} else if (this.duration > IntervalTime.HOUR && this.intervalTime <= IntervalTime.MIN) {
this.restartInterval(IntervalTime.HOUR, TimeUnit.HOUR)
} else if (this.duration > IntervalTime.MIN && this.intervalTime <= IntervalTime.SEC) {
this.restartInterval(IntervalTime.MIN, TimeUnit.MIN)
}
}
restartInterval(intervalTime: IntervalTime, timeUnit: TimeUnit) {
clearInterval(this.intervalId)
this.intervalTime = intervalTime
this.timeUnit = timeUnit
this.intervalId = setInterval(() => {
this.timer()
}, this.intervalTime)
}
get lastTime(): string {
let divide: IntervalTime = IntervalTime.SEC
if (this.duration < IntervalTime.MIN) {
} else if (this.duration < IntervalTime.HOUR) {
divide = IntervalTime.MIN
} else if (this.duration < IntervalTime.DAY) {
divide = IntervalTime.HOUR
} else if (this.duration < IntervalTime.WEEK) {
divide = IntervalTime.DAY
} else if (this.duration < IntervalTime.YEAR) {
divide = IntervalTime.WEEK
} else {
divide = IntervalTime.YEAR
}
return Math.floor(this.duration / divide) + ' ' + this.timeUnit
}
}
</script>
<style lang="scss" scoped></style>
設計のキモ
enum IntervalTime
enum IntervalTime {
SEC = 1000,
MIN = 60 * IntervalTime.SEC,
HOUR = 60 * IntervalTime.MIN,
DAY = 24 * IntervalTime.HOUR,
WEEK = 7 * IntervalTime.DAY,
YEAR = 52 * IntervalTime.WEEK,
}
時間の処理を書く時によく書きがちな60 * 60 * 1000
のような計算を、enumを使って定義しています。どのような計算をしているか一目で分かると思います。このように書くことで「掛ける値の桁が足りてなかった!」などといったちょっとしたミスを減らすことができます。
restartInterval()
restartInterval(intervalTime: IntervalTime, timeUnit: TimeUnit) {
clearInterval(this.intervalObj)
this.intervalTime = intervalTime
this.timeUnit = timeUnit
this.intervalObj = setInterval(() => {
this.timer()
}, this.intervalTime)
}
分表記や時間表記の時に1000ms
で再描画しても無駄なので、経過時間によって、setInterval()
の間隔を長くします。
get lastTime()
get lastTime(): string {
let divide: IntervalTime = IntervalTime.SEC
if (this.duration < IntervalTime.MIN) {
} else if (this.duration < IntervalTime.HOUR) {
divide = IntervalTime.MIN
} else if (this.duration < IntervalTime.DAY) {
divide = IntervalTime.HOUR
} else if (this.duration < IntervalTime.WEEK) {
divide = IntervalTime.DAY
} else if (this.duration < IntervalTime.YEAR) {
divide = IntervalTime.WEEK
} else {
divide = IntervalTime.YEAR
}
return Math.floor(this.duration / divide) + ' ' + this.timeUnit
}
computed
で時間の単位とともに経過時間を出力します。
その他
細かいところはもっと改良できそうな気がするのでお気づきの点があればプルリクもしくはコメント下さい!