さぁ!検索しよう!

HTML,CSS,JavaScriptによるアナログ時計の作り方です。

DEMO

HTMLを書く

始めに以下のHTMLを書きます。

<div class="watch">
  <ul class="point">
    <li><span>1</span></li>
    <li><span>2</span></li>
    <li><span>3</span></li>
    <li><span>4</span></li>
    <li><span>5</span></li>
    <li><span>6</span></li>
    <li><span>7</span></li>
    <li><span>8</span></li>
    <li><span>9</span></li>
    <li><span>10</span></li>
    <li><span>11</span></li>
    <li><span>12</span></li>
  </ul>
  <div class="needle">
    <div id="hour"></div>
    <div id="min"></div>
    <div id="sec"></div>
  </div>
</div>

HTMLは文字盤span、中心軸point、各針needleで構成されています。

CSSを書く

次に以下のCSSを書きます。

html {
  font-size: 24px;
  font-size: 4vw;
}
@media screen and (min-width: 600px) {
  html {
    font-size: 1.5em;
  }
}
body {
  background: #777;
  /*font-size: 8vw;*/
  font-family: 'Vollkorn', serif;
}

.watch {
  background: #333;
  border-radius: 100%;
  color: #fff;
  height: 12em;
  position: absolute;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
  width: 12em;
}

ul {
  background: red;
  border-radius: 100%;
  display: block;
  height: 1em;
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
  width: 1em;
  z-index: 5;
}

li {
  height: 5.5em;
  position: absolute;
  left: 50%;
  bottom: 0.5em;
  margin-left: -0.5em;
  text-align: center;
  -webkit-transform-origin: center bottom;
  transform-origin: center bottom;
  width: 1em;
}

li span {
  display: block;
}

li:nth-child(1) {
  -webkit-transform: rotate(30deg);
  transform: rotate(30deg);
}

li:nth-child(1) span {
  -webkit-transform: rotate(-30deg);
  transform: rotate(-30deg);
}

li:nth-child(2) {
  -webkit-transform: rotate(60deg);
  transform: rotate(60deg);
}

li:nth-child(2) span {
  -webkit-transform: rotate(-60deg);
  transform: rotate(-60deg);
}

li:nth-child(3) {
  -webkit-transform: rotate(90deg);
  transform: rotate(90deg);
}

li:nth-child(3) span {
  -webkit-transform: rotate(-90deg);
  transform: rotate(-90deg);
}

li:nth-child(4) {
  -webkit-transform: rotate(120deg);
  transform: rotate(120deg);
}

li:nth-child(4) span {
  -webkit-transform: rotate(-120deg);
  transform: rotate(-120deg);
}

li:nth-child(5) {
  -webkit-transform: rotate(150deg);
  transform: rotate(150deg);
}

li:nth-child(5) span {
  -webkit-transform: rotate(-150deg);
  transform: rotate(-150deg);
}

li:nth-child(6) {
  -webkit-transform: rotate(180deg);
  transform: rotate(180deg);
}

li:nth-child(6) span {
  -webkit-transform: rotate(-180deg);
  transform: rotate(-180deg);
}

li:nth-child(7) {
  -webkit-transform: rotate(210deg);
  transform: rotate(210deg);
}

li:nth-child(7) span {
  -webkit-transform: rotate(-210deg);
  transform: rotate(-210deg);
}

li:nth-child(8) {
  -webkit-transform: rotate(240deg);
  transform: rotate(240deg);
}

li:nth-child(8) span {
  -webkit-transform: rotate(-240deg);
  transform: rotate(-240deg);
}

li:nth-child(9) {
  -webkit-transform: rotate(270deg);
  transform: rotate(270deg);
}

li:nth-child(9) span {
  -webkit-transform: rotate(-270deg);
  transform: rotate(-270deg);
}

li:nth-child(10) {
  -webkit-transform: rotate(300deg);
  transform: rotate(300deg);
}

li:nth-child(10) span {
  -webkit-transform: rotate(-300deg);
  transform: rotate(-300deg);
}

li:nth-child(11) {
  -webkit-transform: rotate(330deg);
  transform: rotate(330deg);
}

li:nth-child(11) span {
  -webkit-transform: rotate(-330deg);
  transform: rotate(-330deg);
}

.needle {
  height: 8em;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 8em;
}

#hour {
  background: #fff;
  border-radius: 0.4em 0.4em 0 0;
  height: 3em;
  margin-left: -0.4em;
  position: absolute;
  bottom: 4em; /* needleの半分 */
  left: 50%;
  -webkit-transform-origin: center bottom;
  transform-origin: center bottom;
  width: 0.4em;
}

#min {
  background: #fff;
  border-radius: 0.4em 0.4em 0 0;
  height: 4.5em;
  margin-left: -0.2em;
  position: absolute;
  bottom: 4em;
  left: 50%;
  -webkit-transform-origin: center bottom;
  transform-origin: center bottom;
  width: 0.4em;
}

#sec {
  background: red;
  border-radius: 0.2em 0.2em 0 0;
  height: 4.5em;
  margin-left: -0.2em;
  position: absolute;
  bottom: 4em;
  left: 50%;
  -webkit-transform-origin: center bottom;
  transform-origin: center bottom;
  width: 0.4em;
}

#sec:before {
  background: red;
  border-radius: 0 0 0.2em 0.2em;
  content: '';
  display: block;
  height: 1.5em;
  margin-left: -0.1em;
  position: absolute;
  left: 50%;
  top: 100%;
  width: 0.2em;
}

CSSのポイントは以下の通りです。

中心軸のスタイル

ulは赤い中心軸としています。

ul {
  background: red;
  border-radius: 100%;
  display: block;
  height: 1em;
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 50%;
  left: 50%;
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
  width: 1em;
  z-index: 5;
}

文字盤の配置方法

spanは文字盤を表しています。文字盤はliを利用して以下のように配置されます。

1. liを下端を回転軸として絶対配置

liulを基準にしてtransform-originで下端を回転軸として絶対配置します。

2. liを30度間隔で回転させる

lirotateで30度間隔で回転させ、数字であるspanが円周に沿うように配置します。

30度間隔で回転させるコード
li:nth-child(1) {
  -webkit-transform: rotate(30deg);
  transform: rotate(30deg);
}
...
li:nth-child(11) {
-webkit-transform: rotate(330deg);
  transform: rotate(330deg);
}
30度間隔で回転させた結果

rotate

3. 文字盤の傾きを整える

liを回転させるとspanも一緒に回転され、中身の数字が斜めになってしまいます。なので、rotateにマイナスの値を入れて整えています。

li:nth-child(1) span {
  -webkit-transform: rotate(-30deg);
  transform: rotate(-30deg);
}
...
li:nth-child(10) span {
-webkit-transform: rotate(-300deg);
  transform: rotate(-300deg);
}

JavaScriptを書く

最後に以下のJavaScriptを書きます。

(function() {
  var sec = document.getElementById('sec');
  var min = document.getElementById('min');
  var hour = document.getElementById('hour');
  var d = new Date();
  var h = d.getHours();
  var m = d.getMinutes();
  var s = d.getSeconds();

  var Watch = function(t) {
    this.time = t;
    this.deg = (360 / 60);
  }
  Watch.prototype.angle = function() {
    return this.time * this.deg;
  }
  Watch.prototype.hAngle = function() {
    this.hdeg = (360 / 12);
    return this.time * this.hdeg + m * (30 / 60);
  }

  //時計を動かす関数
  var move = function() {
    var d = new Date();
    var h = d.getHours();
    var m = d.getMinutes();
    var s = d.getSeconds();
    var sWatch = new Watch(s);
    var mWatch = new Watch(m);
    var hWatch = new Watch(h);

    sec.style.webkitTransform = 'rotateZ(' + (sWatch.angle()) + 'deg)';
    min.style.webkitTransform = 'rotateZ(' + (mWatch.angle()) + 'deg)';
    hour.style.webkitTransform = 'rotateZ(' + (hWatch.hAngle()) + 'deg)';

  }

  //ループ処理
  function loop() {
    move();
    requestAnimationFrame(loop);
  }
  loop();
})();

JavaScriptのポイントは以下の通りです。

現在の時間・分・秒を取得

現在の時間・分・秒を取得するには始めにnew Date()で現在の日付を取得し、そこから.getHours()で現在の時間・.getMinutes()で現在の分・.getSeconds()で現在の秒を取得します。

var d = new Date();
var h = d.getHours();
var m = d.getMinutes();
var s = d.getSeconds();

それぞれの針の角度を生成するためのクラスを定義しています。秒針と分針は360/60で共に6度ずつ進むため、プロパティにthis.deg = (360 / 60);を追加しています。時針の角度はここでは定義していません。

var Watch = function(t) {
    this.time = t;
    this.deg = (360 / 60);
}

秒針、分針を進めるタイミングを返す関数を定義しています。秒針と分針は共に6度ずつ進みます。なので、その角度に現在の秒、分をそれぞれ掛けることで秒針、分針を進めるタイミングを求めることができます。

Watch.prototype.angle = function() {
    return this.time * this.deg;
}

時針を進めるタイミングを返す関数を定義しています。時針は360/12で30度ずつ進むため、プロパティとしてthis.hdeg = (360 / 12);を追加しています。

続けて、時針を進めるタイミングですが、前述の秒針・分針と同じようにthis.time * this.hdegと計算してしまうと、1時間経過直後に針が進むことになるため不自然な動きになります。

これを防ぐために、あとからm * (30 / 60);を掛けて時針を分刻みにしています。(30/60)は、時針は60分間に30度進むので、30度の間を60分で割ると0.5度となり、これにmを掛けることで1分経過毎に0.5度進みます。

Watch.prototype.hAngle = function() {
    this.hdeg = (360 / 12);
    return this.time * this.hdeg + m * (30 / 60);
}

時計を動かす関数を定義しています。Watchクラスを3つインスタンス化し、それぞれrotateZに対してangle(),hAngle()を実行させています。

var move = function() {
    var d = new Date();
    var h = d.getHours();
    var m = d.getMinutes();
    var s = d.getSeconds();
    var sWatch = new Watch(s);
    var mWatch = new Watch(m);
    var hWatch = new Watch(h);

    sec.style.webkitTransform = 'rotateZ(' + (sWatch.angle()) + 'deg)';
    min.style.webkitTransform = 'rotateZ(' + (mWatch.angle()) + 'deg)';
    hour.style.webkitTransform = 'rotateZ(' + (hWatch.hAngle()) + 'deg)';
  }

ループ関数を定義しています。loop()内でmove()を実行し、続けてrequestAnimationFrame();loop()をを呼ぶことで、loop()内がループされて実行されるようになります。loop()を実行するとループ処理が開始され、時計が永遠と動きます。

 function loop() {
    move();
    requestAnimationFrame(loop);
  }
  loop();

参考文献

以上で、HTML+CSS+JavaScriptでアナログ時計を作る方法を終わります。