数字で表示する時計はデジタル時計として説明に使われます。でもこのページの時計のように数字が連続して流れている場合はアナログ時計だと思います。
昔のテープレコーダーではこのような動きの機械式カウンターがついていて、テープの頭出しに使われました。テープリールの回転をギアで伝えて数字の書いてあるドラムを回転させていたので機構的にもアナログでした。数字がどのぐらい見えなくなったか、次の数字がどのぐらい見えるかもテープの位置を正確に知るには必要なものでした。
これが電卓の数字のようなアナログ表示になった時には数字が突然変わるので、テープの位置を正確に知ることが難しくなりました。なんとなくデジタルになると精確になると認識している人も多いですが、このカウンターの方式の変化を知っている者は、そうは思いません。
1秒の位が9から0になる時に10秒の位が一緒に回転します。これは上記のカウンターの動きを真似たものです。ただ分以上は、面倒なのでパッと変わります。
普通のデジタル時計とどう違うんだっけとピンとこない人は本当にデジタルなデジタル時計をご覧ください。
Javascriptです。
まず、bodyにcanvasを置きます。
<div class="clock"> <canvas id="carea" width="640" height="100"></canvas> </div>
divに入れているのはセンタリングするためですが、描画自体はcanvasの座標なので、ここはご自由に。
div.clock{
text-align:center;
}
canvasはhtml5で追加されたタグです。width属性とheight属性で大きさをピクセルで指定しています。指定しなければ初期値は300×150となります。cssなどで拡大縮小してもキャンバスとしての大きさは指定した(または初期値)を拡大縮小するだけという話ですが、後で確認します。
授業でスクリーンに投影する状態に合わせて640✕100にしていますが、ここで指定した値をプログラムで拾うようにしていますので、好みの値にできます。heightの値を元にフォントサイズを指定していますから、widthはある程度の大きさが必要です。変更してもそれなりに動きます。
ブラウザのウィンドウを小さくすると合わせて小さくなります。canvasは640✕100の解像度のままですが、max-width:100%;のcss指定で画像のように縮小されます。特にモバイルでの表示は、320✕50に縮小されます。プログラムには640,100という値を取得してそれに合わせて描画するほかは、なにも仕掛けをしていません。
idの値はプログラム中の変数値と合わせる必要があります。
htmlファイル内に書いてしまっています。まずは構造だけ
<script type="text/javascript">
function drumclock() {
//時間を取得して数字を書き、
//次の5ミリ秒後に自分自身を呼ぶようセットします
setTimeout('hariclock()',5);
}
/* canvとgなどをグローバル変数にします */
var canv;
var g;
var lasthour,lastmint;
function init(){
//canvとgに値を入れて時計をスタートします
drumclock();
}
/* htmlファイルを読み込んだらinit()を実行します */
window.onload = function() {
init();
}
</script>
init()ではidを頼りにcanvasを認識しますが、htmlが全部読み込まれていないと失敗しますから、onloadで呼び出します。javascriptはvarを付けない変数はグローバルになるので、init()の外のvar宣言はなくても動きますが、グローバルとして意識するために書いています。
時間で繰り返すには、functionの最後にsetTimeout()を書いて自分自身を呼び出すという手法が定番です。5は5ミリ秒です。5ミリ秒は経験的になめらかに動かすために決めた値です。
5ミリ秒ごとに繰り返す部分。
時分秒、ミリ秒を取り出すのは針式時計のプログラムと共通ですが、次の秒の数字を加えて回すために現在の秒に1を足した次の秒を計算し、10の位、1の位に分割しています。
canvasの高さをフォントの大きさとし、その2/3を数字1つの幅としています。パイカピッチの想定ですが、日本語フォントの場合は1/2の方が合います。また、プロポーショナルなフォントでは小さめになる傾向があります。
ただ文字の幅を取得するのは面倒なので、これで手を打ちました。":"は文字としては細身でプロポーショナルなフォントでは位置がずれるので、円を2つ描いて画像として置いています。
function drumclock() {
var now = new Date();
var hour = now.getHours();
var mint = now.getMinutes();
var sec = now.getSeconds();
var secnow = ("0"+sec.toString()).slice(-2);
var ssnow1 = secnow.substr(0,1);
var ssnow2 = secnow.substr(1,1);
var nextsec = sec+1;
if (nextsec==60) nextsec=0;
var secnxt = ("0"+nextsec.toString()).slice(-2);
var ssnxt1 = secnxt.substr(0,1);
var ssnxt2 = secnxt.substr(1,1);
var msec = now.getMilliseconds();
var h = canv.height;
var w = canv.width;
var yb = h*0.9;
g.font = Math.floor(h)+ "px 'DejaVu Serif'";
var fw = h*2/3;
var xbgn = (w-fw*8)/2
if (lasthour != hour){
g.clearRect( 0, 0, xbgn+fw*3, h );
g.fillText(("0"+hour.toString()).slice(-2),xbgn,yb);
// g.fillText(":",xbgn+fw*2.2,h*0.8);
g.beginPath();
g.arc(xbgn+fw*2.4, h*4/10, fw*0.08, 0, Math.PI*2,true);
g.arc(xbgn+fw*2.4, h*7/10, fw*0.08, 0, Math.PI*2,true);
g.closePath();
g.fillStyle = '#000000';
g.fill();
}
if (lastmint != mint){
g.clearRect( xbgn+fw*3, 0, xbgn+fw*6, h );
g.fillText(("0"+mint.toString()).slice(-2),xbgn+fw*3,yb);
g.beginPath();
g.arc(xbgn+fw*5.4, h*4/10, fw*0.08, 0, Math.PI*2,true);
g.arc(xbgn+fw*5.4, h*7/10, fw*0.08, 0, Math.PI*2,true);
g.closePath();
g.fillStyle = '#000000';
g.fill();
}
g.clearRect( xbgn+fw*6, 0, w, h );
if (ssnow1 == ssnxt1){
g.fillText(ssnow1 ,xbgn+fw*6,yb);
}else if( h-msec/1000*h > yb){
g.fillText(ssnow1 ,xbgn+fw*6,yb);
}else{
g.fillText(ssnow1 ,xbgn+fw*6,h -msec/1000*h);
g.fillText(ssnxt1 ,xbgn+fw*6,h*2-msec/1000*h);
}
g.fillText(ssnow2 ,xbgn+fw*7,h -msec/1000*h);
g.fillText(ssnxt2 ,xbgn+fw*7,h*2-msec/1000*h);
lasthour = hour;
lastmint = mint;
setTimeout('drumclock()',5);
}
全体の構造で示したグローバル変数とid="carea"で得るcanvasのオブジェクトと、そのコンテキストgでを代入しておきます。lasthour,lastmint は変化していない時に書き換えないために使用します。
var canv;
var g;
var lasthour,lastmint;
function init(){
canv = document.getElementById('carea');
g = canv.getContext('2d');
drumclock();
}
window.onload = function() {
init();
}