こちらの続きです。
デジタル時計作ったならやっぱりアナログ時計も作っておかないと…。
というわけで完成品がコチラ。
今回はアニメーションは使っておりません。
各針は角度をミリ秒単位で計算して動かしています。
構成図
針(Hand)と文字盤(Dials)の2つでアナログ時計(AnalogClock)を構成していきます。
非常にシンプルですね。
Hand
Hand.vue
<template>
<div class="hand" :class="type" :style="{'transform': 'rotate(' + rotate + 'deg)'}" />
</template>
<script>
export default {
props: {
rotate: 0,
type: ''
},
}
</script>
<style scoped>
.hand {
background-color: rgba(255,255,255,0.8);
}
.seconds {
width: 1px;
height: 150px;
transform-origin: 0px 130px;
}
.minutes {
width: 3px;
height :150px;
transform-origin: 1.5px 130px;
}
.hours {
width: 5px;
height: 100px;
transform-origin: 2.5px 80px;
}
</style>
typeで針の種類、秒針(seconds)、分針(minutes)、時針(hours)を指定します。
rotateに針の角度を指定し、styleバインディングで動的に回していきます。
Dials
Dials.vue
<template>
<div class="dials">
<div
v-for="n in 60"
:key="n"
:style="{
'top':top(n) + 'px',
'left': left(n) + 'px',
'transform': 'rotate(' + rotate(n) + 'deg)'}"
:class="['scale', {'fifth': n % 5 == 0}]">
<p v-if="n % 5 == 0"
:style="{'transform': 'rotate(' + -rotate(n) + 'deg)'}">
{{ n / 5 }}
</p>
</div>
</div>
</template>
<script>
export default {
methods: {
top(val) {
return 150 - Math.cos(Math.PI / 30 * val) * 150
},
left(val) {
return 150 + Math.sin(Math.PI / 30 * val) * 150
},
rotate(val) {
return 6 * val
}
}
}
</script>
<style scoped>
.dials {
background-color: #333;
border-radius: 300px;
position: relative;
height: 300px;
width: 300px;
}
.scale {
background-color: white;
position: absolute;
width: 1px;
height: 10px;
transform-origin: left top;
}
.fifth {
width: 5px;
height: 20px;
}
.fifth p {
margin-top: 20px;
margin-left: -15px;
color: white;
font-size: 28px;
font-weight: bold;
text-align: center;
width: 35px;
}
</style>
60個の目盛をループで位置(left, top)と向き(rotate)を計算しながら設定しています。
位置に関しては以下のように高校数学で学んだことを活用します。(懐かしい…)
あとは5の倍数で目盛を太くして文字を表示しているだけです。
AnalogClock
AnalogClock.vue
<template>
<div class="clock">
<dials />
<hand class="hand seconds" type="seconds" :rotate="seconds"/>
<hand class="hand minutes" type="minutes" :rotate="minutes" />
<hand class="hand hours" type="hours" :rotate="hours"/>
</div>
</template>
<script>
import Hand from './Hand'
import Dials from './Dials'
import moment from 'moment'
export default {
components: {
Hand,
Dials
},
data() {
return {
intervalId: undefined,
time: undefined
}
},
computed: {
seconds() {
let ss = moment(this.time).seconds()
let nn = moment(this.time).milliseconds()
return 6 * (ss + nn / 1000)
},
minutes() {
let mm = moment(this.time).minutes()
return 6 * (mm + this.seconds / 360)
},
hours() {
let hh = moment(this.time).hours()
return 30 * (hh + this.minutes / 360)
}
},
methods: {
setTime() {
this.intervalId = setInterval(() => {
this.time = new Date()
}, 10)
}
},
mounted() {
this.setTime()
},
beforeDestroy() {
clearInterval(this.intervalId)
},
}
</script>
<style scoped>
.clock {
position: relative;
}
.hand {
position: absolute;
}
.seconds {
left: 150px;
top: 20px;
}
.minutes {
left: 149px;
top: 20px;
}
.hours {
left: 148px;
top: 70px;
}
</style>
時刻の取得についてはデジタル時計編を参照してください。
針の動きを滑らかにするためにミリ秒を含めて角度の計算をしています。
簡単に解説をすると、例えば秒針の場合、秒の値だけだと0°→6°→12°→… というように角度が飛んでしまいます。
それをミリ秒を含めることで補完しています。
transitionでも滑らかに動かせますが、360°になるときに考慮が必要になります。
おわりに
さすがにもう時計シリーズはいいかな…。