時刻を描画しよう。

文字盤はできました。いよいよ今度は時間を表す“針”を描画します。“針”を描画するにも、先ほど行った文字盤への目盛りの描画と同じようなやり方が使えます。時刻にそって角度を計算しラジアンに変換、そしてcanvasの座標レイヤーを回転させてパスを定義し描画します。

始まりは0時に

前章のcontext.arc(x, y, radius, startAngle, endAngle, anticlockwise)メソッドの説明のときに書きましたが、Canvasで円を扱う場合デフォルトではx軸の正の方向を向いています。時計でいうと3時の方向です。わかっていればそれはそれでいいのですけど、時計を描画するには少々面倒です、3時を基準にして計算しなければなりませんからね。ここはいっそ、“針”を描画するcanvas要素timeの座標レイヤーは最初に0時の方向に向けてしまいましょう。

3時の向きを0時の向きにするのですから、時計回りに270度回転させるか、反時計回りに90度回転させればいいですね。

function SampleClock() {
    ...
    this.boardContext.translate(this.width() / 2, this.height() / 2);
    this.context.translate(this.width() / 2, this.height() / 2);

    this.context.rotate(this.toRad(-90));
    ...
}

これもコンストラクタの中でやってしまいましょう。

“時”を刻む

それでは実際に現在時刻を“針”で指していきますが、その為にはまず現在時刻を取得しなければなりません。JavaScriptで現在時刻を取得するにはDateオブジェクトを使います。newを使ってインスタンス化します。

var now = new Date();
var h = now.getHours();
var m = now.getMinutes();
var s = now.getSeconds();
var ms = now.getMilliseconds();
  • Date.getHours()

    “時”を返します。

  • Date.getMinutes()

    “分”を返します。

  • Date.getSeconds()

    “秒”を返します。

  • Date.getMilliseconds()

    “ミリ秒”を返します。1000ミリ秒で1秒になります。

ここで“針”についてちょっと考えてみましょう。それぞれの“針”は次の単位の時間の影響も受けます。短針は“時”だけを指すのではなくって、“分”の分の角度も加えて表示します。長針は“秒”の影響を受けますし、秒針は“ミリ秒”の影響を受けます。秒針は違う場合も多いですが、実世界のアナログ時計もそういう風な動きをしています。それに習うとしましょう。

繰り返しになりますが、短針を描画するにはその時刻の0時から短針までの角度を求めてラジアン値に変換し、それを使ってcanvasを回転させます。そしてサブパスを定義して線を引きます。でも、それを長針、秒針と続けて行いますから少し注意が必要です。短針を描画するのにcanvasを回転させたので、そのまま長針の角度を算出して回転させてしまうと、既に回転していた短針の分だけ余計に回転してしまう事になります。長針のために回転させる前に短針の分を逆に回転させても辻褄は合いますが、Canvasには現在の状態を保持しておいて後から取り出す機能があるのでそれを活用しましょう。それにはcontext.save()context.restore()メソッドを使います。

Note

context.save()で保持される状態は、変形マトリックス、クリップ領域、幾つかの属性値となっています。context.rotate()での回転は変形マトリックスに含まれますから状態を保持されます。

また、今回は一つしか扱いませんが、保存領域はスタックになっているので複数の状態を保持する事もできます。

短針を描画する例は以下のようになりました。

SampleClock.prototype = {
    ...
    draw_time: function () {
        ...
        var now = new Date();
        var rad = this.hourRad(now);
        this.context.save();
        this.context.beginPath();
        this.context.lineWidth = this.radius() * 0.06;
        this.context.rotate(rad);
        this.context.moveTo(0, 0);
        this.context.lineTo(this.radius() * 0.5, 0);
        this.context.stroke();
        this.context.restore();
        ...
    },
    ...
}

context.beginPath()を始めるまえにcontext.save()で状態を保存します。このとき保持されるcanvasはまだ0時の方向を向いています。その後canvasを回転させパスを定義していきcontext.stroke()で描画します。このときcanvasは短針の方向を向いていますが、最後に保存していた状態に復帰します。これでまた0時の方向を向きます。

Warning

実際にはcontext.save()で保持される状態にパスは含まれませんから、context.beginPath()の前に実行する必要はなくその後ろでもいいのですけど、区切りがいいのでこの位置にしました。

SampleClock.hourRad(datetime)メソッドは、現在の時刻を受け取り内部で“時”と“分”を取得し、それらを元に0時からの角度を算出しラジアン値を返します。角度の求め方は各自考えてみてください。1時間当たり何度回転するのか、また1分当たり何度回転するのか。1時30分のとき、短針は1時と2時の丁度中間を指すように計算しましょう。

Note

実は気にしなくても表示上は関係ありませんが、今回は12時間時計ですので13時以降の時間のときは12時以下の時間に変換してあげましょう。変換方法も幾つかありますね。

Note

この時点ではブラウザをリロードしてindex.htmlを更新しても“針”は表示されません。sampleClock.jsの最後に

clock.draw_time();

と記述するか、ブラウザのコンソールを開いて直接入力するなどして呼び出してください。

“分”を刻む

長針も同じようなやり方で描画しますが、わかりやすいように短針よりも長く細く描画しましょう。“針”の角度に影響を与えるのは“分”と“秒”です。

“秒”を刻む

秒針もこれまでと同じようなやり方が使えます。さらに“針”に工夫してみましょう。私は細く長く、そして赤くしてみました。

Note

描画する際の色を指定するにはcontext.strokeStylecontext.fillStyle属性を変更します。指定する色はHTMLでも用いる16進数表記や、CSSで使えるrgba(r, g, b, a)表記などが使えます。

this.context.strokeStyle = '#ff0000';
針

こんな感じになると思います!

Warning

こちらも小細工しているので全く同じにはなりません。線の開始位置を変えたりしてます。

刻み続ける

これでページを表示した時点の時刻を描画できるようになりました。後はこれを連続的に実行し続ければ、人間の目の錯覚により動いているように見えるはずです。それにはSampleClock.draw_time()メソッドを呼び続ければよさそうです。でも、もう一つだけ問題があります。

連続してSampleClock.draw_time()メソッドを呼び出すと、確かに繰り返しそのときの時刻が描画されますが、前回までに描いた時刻も描画されたままなので“針”がどんどんと大きくなっていくだけで全然時計っぽくありません。そのうち画面は真っ黒になってしまいます。これはこれで見ていて楽しい気もしますが、何の事だかさっぱりです。これを解決するには重ねて“針”を描画しなければいいだけなので、新たに“針”を描画をする前に全てを消しさってしまいましょう。それにはcontext.clearRect(x, y, w, h)を使います。

context.clearRect(x, y, w, h);
  • x

    消去したい矩形領域の左上端のx座標を指定します。

  • y

    消去したい矩形領域の左上端のy座標を指定します。

  • w

    消去したい矩形領域の幅の値を指定します。

  • h

    消去したい矩形領域の高さの値を指定します。

時刻を描画しているcanvas要素timeの座標レイヤーの原点は中央にあるので、消去したい矩形領域の左上端の座標ははそれぞれの幅の半分を除いたものになります。消去したい矩形領域の幅はcanvasの大きさそのままです。描画する前に実行したいのでSampleClock.draw_time()メソッドの先頭へ実装する事にします。

SampleClock.prototype = {
    ...
    draw_time: function () {
        this.context.clearRect(-this.width() / 2, -this.height() / 2, this.width(), this.height());
        ...
    },
    ...
}

さぁ、これで全ての準備が整いました。js/sampleClock.jsの最後の行にあるtik()関数の呼び出しをコメントアウトして実行してみましょう!

Note

ちなみに、コンソールからtok()関数を実行すると時計を止める事もできます。デバッグ時などにどうぞ :-)

Table Of Contents

Previous topic

文字盤を描画しよう。

Next topic

いろんな表現方法。

This Page