用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>

猜你喜欢

转载自blog.csdn.net/jspyth/article/details/83216853