canvasで時計を作る

2020.09.16.水
JavaScript

開発部の柳井です。withコロナで大変な状況が続いていますが、弊社ではリモートワークなどのコロナ対策をしながら、サービスを提供し続けています。出社してる時は34インチの曲面ディスプレイを使っていますが、自宅には置く場所が無いため、代わりに13.3インチのモバイルモニタを使っています(macは13インチ)(経費で補助が出ました!)。

さて本題ですが、今回はcanvasで時計を作ります。デジタル時計も後の方でやりますが、時間を取得して数字を表示するだけなので、メインはアナログ時計です。コーディングには前回はCodePenを使いましたが、モブプログラミングの時に知ったRepl.itを使います。ヘッダーなどが無いのですっきりしてます。

canvasとは

canvasはHTML5で追加された要素で、図形を描画することができます。似たような技術にSVGがあります。こちらは<svg>要素の中に<circle>などを書いていきます。実際に自分が携わっている案件でも、データの可視化にD3.jsを使っていますが、今回はcanvasで「円を描く」とか「線を引く」というメソッドを書いていく方法を使います。

今回は、高校の数学Ⅱ(?)でやった、三角関数を使います。忘れた人はこちらのようなサイトで復習してください。



第一段階。真ん中にエディタ、右に実行結果、左にファイル一覧が表示されますが、単体で開くこともできます。

まずは時計の枠として円を描きます。



canvasでの描画は、描画用のエリアを用意してから、beginPath()→円など図形の点・パス(経路)を指定する→実際にstroke()またはfill()で描くという流れです。draw()では最初にclearRect()でcanvasをクリアします。第一・第二段階ではしていませんが、draw()はタイマーで1秒に1回描画し直すので最初にクリアする必要があります。

円弧を描くarc()では中心のx座標、y座標、半径、何度から何度まで、時計回りor反時計回りを指定します。360度(=2π)なので深く考えずに描けば良いです。詳しくはドキュメントを見てください。座標を指定して、どのくらいの太さ・色で描画するかを指定する方法は、canvas(JavaScript)だけでなくDXライブラリなどグラフィックス描画ライブラリではよくあるパターンなので、慣れると簡単です。

目盛り

目盛りというのは、1〜12の目盛りと、間にある4本の目盛りです。先ほどは2πを指定しましたが、今度は2π/60ごとに1本の線を引きます。以下の図のように、円の中心から円の縁までの弦の90%程度の点を(A)とし、そこから円の縁(B)まで線を引きます。

線は、ペンをmoveTo()で始点(A)へ移動させ、lineTo()で終点(B)まで線を引くイメージです。(A)や(B)の座標を決めるには、2π/60 * n(n=0〜59の整数)をを使えばOKです。単位円r=1を任意の長さにし、x座標にはcosθ、y座標にはsinθを使いましょう。iはひとまず10までにしています。


12時から始まると思ったら、3時から始まりましたね?canvasのxy座標系は左上が0で、xは右、yは下へ+になります。また、数学の三角関数は反時計回りなので、それらを考慮すると、calcRadian()を以下のようにすると感覚的に扱えるようになります。


目盛りも、枠と同様に2π一周描くので、どこから開始してどっち周りでも描けますが、ここでラジアンの問題を解決しておくと後が楽です。

時針

針は時針、分針、秒針がありますが、まず時針から描きます。12時間なので、1時間で2π/12動きます。目盛りの描き方を真似すれば余裕ですね!例えば10時16分33分にしてみましょう。




ここから第二段階です。

分針

次は分針。60分なので、1分に2π/60動きます。hour / 12の部分をminute / 60にすると以下のようになります。

なんか違和感ありませんか?10時59分にしてみてください。

11時になると、時針がいきなり2π/12動いてしまいます。時針は正しくは、1時間で2π * (hour + (minute / 60)/12)動くようにしないといけないのでした。もっとちゃんとしたい方はsecondも入れてもらえばいいですが、画面でそんなに見えるものではないのでここでは省いています。

秒針

秒針は60秒なので、1秒に2π/60動きます。これより小さい数字は考えなくていいので、second / 60だけです。色や太さを変えてこんな感じになりました。




ここから第三段階(これで終わり)です。

仕上げ

現在時刻を取得し、タイマーで1秒に1回draw()を実行するようにします。針が中心より反対側にも伸びている(-rとしてhandSecondLength2)などを良い感じに実装して完成です。詳しくはコードを読んでください。

おまけ

デジタル時計も付けてみました。取得した時・分・秒が1桁の場合は"0"の文字列と連結して0埋めをしましょう。デジタルっぽいフォントは、7セグ・14セグフォント 「DSEG」を使用しました。

まとめ

「canvas 時計」で検索するとたくさん記事が公開されています。CSSに凝ってたりするのが面白いです。皆さんも作ってみてください。



キー・ポイントでは、いろいろなことに挑戦する意欲のある方を募集しています。

柳井裕子

柳井裕子

2016年入社、開発部所属。主にWatasoon、WebFile(自社サービス)の他、受託案件にも携わっています。開発日誌ではVRオフィス見学などを投稿してます。