1. 程式人生 > >用canvas製作一個時鐘

用canvas製作一個時鐘

我想熟悉一下canvas的使用,參考一個例子做了一個能動態展示當前時刻的canvas時鐘。

先看效果:

實現思路:

使用canvas繪圖第一步肯定是先獲取canvas元素物件,並定義上下文。

var clock = document.getElementById("clock"); clock.style.backgroundColor = "black"; var graph = null; graph = clock.getContext("2d");

然後開始繪製內外圓:

beginPath()方法是路徑的開始,stroke()是繪製前面beginPath()之間定義的路徑,假如你想畫兩條兩條顏色等設定不一樣的路徑,那你就需要各自用一個beginPath()去開始路徑,否則在同一個beginPayh()後面的兩個不同的strokeStyle樣式只能表現出後定義的那一個設定。比如說我在下面程式碼中對畫內圓部分前加一行graph.strokeStyle = "white"的話,得不到兩個不同顏色的圓,兩個圓都會是白色的。

graph.beginPath(); //畫外圓 graph.strokeStyle = "yellow"; graph.moveTo(600,300); graph.arc(300,300,outside_circle_r,0,2*Math.PI,false); //畫內圓 graph.moveTo(590,300); graph.arc(300,300,inside_circle_r,0,2*Math.PI,false); graph.stroke();

刻度:

每一條線段的繪製都需要知道兩個端點的座標,刻度端點的座標就需要使用三角函數了,可以以預設的左上角為原點,也可以使用translate方法把座標原點移動到時鐘的圓心,後者計算座標更簡單。但是我用的是笨方法,以左上角為座標原點來想辦法計算各個刻度的座標值,小時刻度和分鐘刻度長度不一樣,但座標計算方法是一樣的,我寫了一個函式drawHourAndMinuteLine去封裝了計算過程,然後針對小時刻度和分鐘刻度用對應傳參呼叫了兩次。刻度部分我是在看參考例子前做的,以畫布左上角作為座標原點,實現過程比較複雜,其實沒有必要,所以就不解釋這部分我寫的程式碼了。這一部分可以參考文章開頭的參考例子連結,以時鐘中心點作為圓點更簡單。

//畫小時刻度 var hour_len = 30;//刻度長度 var hourAngleArray = []; for(let i = 0;i < 4;i++){ hourAngleArray.push(i*Math.PI/6); } drawHourAndMinuteLine(hourAngleArray,hour_len); //畫分鐘刻度 var minuteAngleArray = []; var minute_len = 10;//刻度長度 for(let i = 0;i < 15;i++){ minuteAngleArray.push(i*Math.PI/30); } drawHourAndMinuteLine(minuteAngleArray,minute_len);

文字:

繪製文字的基本過程都是相同的,不同的只是樣式,座標引數,所以我也寫了函式封裝。

function drawText(fillStyle,font,textAlign,textBaseline,size,x,y){ graph.fillStyle = fillStyle; graph.font = font; graph.textAlign = textAlign; graph.textBaseline = textBaseline; graph.fillText(size,x,y); }

比如說我繪製上下左右的12、6、9、3數字文字的時候就直接呼叫這個函式:

//畫刻度文字值 drawText("yellow","bold 18px Arial","center","middle","12",300,48); drawText("yellow","bold 18px Arial","center","middle","3",552,300); drawText("yellow","bold 18px Arial","center","middle","6",300,552); drawText("yellow","bold 18px Arial","center","middle","9",48,300);

時針、分針和秒針:

為了實現在當前時刻時針指向正確數字的功能實現,必須把時間和時針轉過的角度之間建立一個關係,還要確定每根針的長度。這一部分參考了例子,以時鐘圓心作為原點實現。

首先獲得時間,並在角度和時間之間建立數學關係:

var hourLength = 180; var minuteLength = 204; var secondLength = 240; var date = new Date(); var hour = date.getHours(); var minutes = date.getMinutes(); var seconds = date.getSeconds(); if (hour > 12) { hour -= 12; } var hourAngle = (hour * 30 - 90) * Math.PI / 180; var minutesAngle = (minutes * 6 - 90) * Math.PI / 180; var secondsAngle = (seconds * 6 - 90) * Math.PI / 180;

繪製三根針:

//分針 graph.save(); graph.beginPath(); graph.translate(300, 300); graph.strokeStyle = "yellow"; graph.moveTo(0, 0); graph.lineTo(minuteLength * Math.cos(minutesAngle), minuteLength * Math.sin(minutesAngle)); graph.stroke(); //秒針 graph.save();//儲存新的圓點 graph.beginPath(); graph.strokeStyle = "red"; graph.moveTo(0, 0); graph.lineTo(secondLength * Math.cos(secondsAngle), secondLength * Math.sin(secondsAngle)); graph.stroke(); graph.stroke(); //時針 graph.beginPath(); graph.strokeStyle = "blue"; graph.moveTo(0, 0); graph.lineTo(hourLength * Math.cos(hourAngle), hourLength * Math.sin(hourAngle)); graph.stroke(); graph.restore(); graph.restore();

上面程式碼我用到了兩次save()和兩次restore()方法,save()方法用於儲存前面的各種設定,restore()方法用於退回到上一次save()所儲存的設定。因為在我的程式碼中,只有繪製三根針的時候原點設定才是時鐘的中心,所以我的使用主要是為了保證只有在這一部分程式碼中,原點的設定是時鐘中心。

時鐘上方的報時文字:

文字繪製還是使用前面提到的一個自定義函式,但需要隨著時間更新文字,如果只是簡單地每秒繪製一次文字,那不同文字會在同一個區域重疊,所以必須在繪製新的文字時把前一秒繪製的文字去掉,我的辦法是每次畫一個和背景色相同顏色的矩形框把文字蓋住。

/*畫出時間文字並定時清除*/ function drawTimeText(){ var date = new Date(); var timeText1 = date.getHours()+":"+date.getMinutes()+":"+date.getSeconds(); var timeText2 = date.getFullYear()+"年"+(date.getMonth()+1)+"月"+date.getDate()+"日"; drawText("yellow","bold 30px Arial","center","top",timeText1,295,162); drawText("yellow","bold 16px Arial","center","top",timeText2,295,200); setTimeout("drawClearRect()",980);//清除矩形區域的內容 } function dateText(time){ graph.beginPath(); //表內文字 drawTimeText(); setInterval("drawTimeText()",time); graph.stroke(); graph.restore(); }

時針變化:

最後我把前面所有繪製部分的程式碼放到一個函式裡,並新增兩行清除整個時鐘的程式碼,使用的是clearRect()方法清除區域,否則你會得到一圈60根秒針,然後用setInterval()每秒呼叫一次這個函式。

if (clock.getContext) { drawClock(); dateText(1001); setInterval(drawClock, 1000); }

上面dateText給的時間1001ms是為了把顯示報時文字的時間和drawClock這個繪製整個時鐘的函式中清除整個時鐘的時刻錯開,但這也是一個bug,隨著時間它所引起的時間差會越來越大,暫時沒有想到怎麼解決這個問題。

完整程式碼:

<!DOCTYPE html>

<html>

<head lang="zh-CN">

<meta charset="UTF-8">

<title>Clock</title>

</head>

<body>

<canvas id="clock" width="600px" height="600px">

Canvas

</canvas>

<script type="text/javascript">

var clock = document.getElementById("clock");

clock.style.backgroundColor = "black";

var graph = null;

var outside_circle_r = 300;/*外圓半徑*/

var inside_circle_r = 290;/*內圓半徑*/

if (clock.getContext) {

drawClock();

dateText(1001);

setInterval(drawClock, 1000);

}

function drawClock() {

var hourLength = 180;

var minuteLength = 204;

var secondLength = 240;

var date = new Date();

var hour = date.getHours();

var minutes = date.getMinutes();

var seconds = date.getSeconds();

if (hour > 12) {

hour -= 12;

}

var hourAngle = (hour * 30 - 90) * Math.PI / 180;

var minutesAngle = (minutes * 6 - 90) * Math.PI / 180;

var secondsAngle = (seconds * 6 - 90) * Math.PI / 180;

graph = clock.getContext("2d");

/*先清除整個圖*/

graph.clearRect(0, 0, clock.width, clock.height);

graph.strokeStyle = "black";

graph.save();

graph.beginPath();

//畫外圓

graph.strokeStyle = "white";

graph.moveTo(600,300);

graph.arc(300,300,outside_circle_r,0,2*Math.PI,false);

//畫內圓

graph.moveTo(590,300);

graph.arc(300,300,inside_circle_r,0,2*Math.PI,false);

graph.stroke();

//畫三個指標的交接處

graph.beginPath();

graph.strokeStyle = "#ff0000";

graph.moveTo(305,300);

for(let i = 0;i <= 10;){//填充圓裡面的顏色

graph.arc(300,300,i,0,2*Math.PI,false);

i += 0.5;

}

graph.stroke();

//畫小時刻度

var hour_len = 30;//刻度長度

var hourAngleArray = [];

for(let i = 0;i < 4;i++){

hourAngleArray.push(i*Math.PI/6);

}

drawHourAndMinuteLine(hourAngleArray,hour_len);

//畫分鐘刻度

var minuteAngleArray = [];

var minute_len = 10;//刻度長度

for(let i = 0;i < 15;i++){

minuteAngleArray.push(i*Math.PI/30);

}

drawHourAndMinuteLine(minuteAngleArray,minute_len);

//畫刻度文字值

drawText("yellow","bold 18px Arial","center","middle","12",300,48);

drawText("yellow","bold 18px Arial","center","middle","3",552,300);

drawText("yellow","bold 18px Arial","center","middle","6",300,552);

drawText("yellow","bold 18px Arial","center","middle","9",48,300);

//分針

graph.save();

graph.beginPath();

graph.translate(300, 300);

graph.strokeStyle = "yellow";

graph.moveTo(0, 0);

graph.lineTo(minuteLength * Math.cos(minutesAngle), minuteLength * Math.sin(minutesAngle));

graph.stroke();

//秒針

graph.save();//儲存新的圓點

graph.beginPath();

graph.strokeStyle = "red";

graph.moveTo(0, 0);

graph.lineTo(secondLength * Math.cos(secondsAngle), secondLength * Math.sin(secondsAngle));

graph.stroke();

graph.stroke();

//時針

graph.beginPath();

graph.strokeStyle = "blue";

graph.moveTo(0, 0);

graph.lineTo(hourLength * Math.cos(hourAngle), hourLength * Math.sin(hourAngle));

graph.stroke();

graph.restore();

graph.restore();

}

/*時間文字*/

function dateText(time){

graph.beginPath();

//表內文字

drawTimeText();

setInterval("drawTimeText()",time);

graph.stroke();

graph.restore();

}

/*畫出時間文字並定時清除*/

function drawTimeText(){

var date = new Date();

var timeText1 = date.getHours()+":"+date.getMinutes()+":"+date.getSeconds();

var timeText2 = date.getFullYear()+"年"+(date.getMonth()+1)+"月"+date.getDate()+"日";

drawText("yellow","bold 30px Arial","center","top",timeText1,295,162);

drawText("yellow","bold 16px Arial","center","top",timeText2,295,200);

setTimeout("drawClearRect()",980);//清除矩形區域的內容

}

function drawText(fillStyle,font,textAlign,textBaseline,size,x,y){

graph.fillStyle = fillStyle;

graph.font = font;

graph.textAlign = textAlign;

graph.textBaseline = textBaseline;

graph.fillText(size,x,y);

}

/*畫一條線段的函式*/

function drawStraightLine(color,X,Y,toX,toY){

graph.beginPath();

graph.strokeStyle = color;

graph.moveTo(X,Y);

graph.lineTo(toX,toY);

graph.stroke();

}

/*畫出時間刻度*/

function drawHourAndMinuteLine(timeType,len){

for(let i = 0;i < timeType.length;i++){

let x = outside_circle_r+inside_circle_r*Math.cos(timeType[i]);

let y = outside_circle_r-inside_circle_r*Math.sin(timeType[i]);

let toX = x-len*Math.cos(timeType[i]);

let toY = y+len*Math.sin(timeType[i]);

let toXLeftTop = outside_circle_r-inside_circle_r*Math.cos(timeType[i])+len*Math.cos(timeType[i]);

let toYRightBottom = outside_circle_r+inside_circle_r*Math.sin(timeType[i]);

drawStraightLine("white",x,y,toX,toY);//右上刻度

drawStraightLine("white",outside_circle_r-inside_circle_r*Math.cos(timeType[i]),y,toXLeftTop,toY);//左上刻度

drawStraightLine("white",x,toYRightBottom,toX,toYRightBottom-len*Math.sin(timeType[i]));//右下刻度

drawStraightLine("white",outside_circle_r-inside_circle_r*Math.cos(timeType[i]),toYRightBottom,toXLeftTop,toYRightBottom-len*Math.sin(timeType[i]));//左下刻度

}

}

/*清除文字時間顯示區*/

function drawClearRect(){

graph.clearRect(235,162,120,55);

}

</script>

</body>

</html>