这两天大概看了一下canvas的绘图方式,决定先做一个时钟练练手,以前数学没学好,计算圆坐标的公式完全不会,在绘制刻度的地方花费了很多时间,在此记录下整个步骤,方便以后参考。你可以点击这里查看demo
准备工作:先分析时钟的组成部件:
1.表盘:由五个圆形叠加
2.刻度:2种直线
3.数字:1-12
4.指针:3种直线
5.日历:矩形+数字
一、首先建立画布
html:
1 2 |
<canvas id="canvas" width="350" height="350"> </canvas> |
js:
1 2 3 4 5 6 |
var can = document.getElementById('canvas'); var locText = document.getElementById('location'); var ctx=can.getContext("2d"); // 定义圆心坐标为画布中心点坐标 var a = canvas.width/2; var b = canvas.height/2; |
二:绘制表盘:
1.因为表盘是由若干个圆形组成的,所以这里先定义一个绘制圆形的函数:
grdcircle(a,b,半径,渐变色1,渐变色2);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 以(a,b)处为圆心绘制一个半径为r的圆形,并用线性渐变填充 function grdcircle(a,b,r,color1,color2){ ctx.beginPath(); var grd=ctx.createLinearGradient(150,0,150,300); grd.addColorStop(0,color1); grd.addColorStop(1,color2); ctx.shadowOffsetX=0; ctx.shadowOffsetY=0; ctx.shadowBlur=0; ctx.arc(a,b,r,0,2*Math.PI); ctx.fillStyle=grd; ctx.fill(); ctx.closePath(); } |
2.使用刚才建立的函数依次绘制四个不同大小的圆形,并用渐变色填充
1 2 3 4 |
grdcircle(a,b,150,"#333","#000"); grdcircle(a,b,144,"#999","#ccc"); grdcircle(a,b,134,"#666","#999"); grdcircle(a,b,133,"#fff","#dedede"); |
三、绘制刻度
刻度是圆上某点到圆心的连线上的一小截线段,将整个圆分为60等份,则每个刻度到圆心的连线之间的夹角为360°/60=6°
已知圆上任意点的坐标公式为:
x=a+r*sin(c*π/180);
y=b+r*sin(c*π/180);
(a,b)=圆心坐标,r=半径,c=圆心角
圆心坐标,半径和圆心角都是已知,那么现在只要计算出点A和点B的坐标,就可以画出刻度了,这里使用for循环,每隔6°画一条点A(t,u)到点B(v,c)的线段;
scale(半径,宽度);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function scale(r,width){ for(var p=1;p<=60;p++) { var o=p*6; t=a+r*Math.cos(Math.PI/180*o); u=b+r*Math.sin(Math.PI/180*o); ctx.lineWidth=width; v=a+(r-6)*Math.cos(Math.PI/180*o); c=b+(r-6)*Math.sin(Math.PI/180*o); ctx.beginPath(); ctx.strokeStyle="#000000"; ctx.lineCap="round"; ctx.moveTo(t,u); ctx.lineTo(v,c); ctx.stroke(); ctx.closePath(); } } scale(131,1); |
这样画出来每一个刻度都一样长,但因为钟表刻度每5个刻度是一条长线段,所以还要判断当前刻度值/30是否是整数,如果是整数,则绘制一条长度12,宽度2的粗线,如果不是则绘制长度6,宽度1的细线。
这里先定义一个判断整数的函数
1 2 3 |
function isInteger(obj) { return Math.floor(obj) === obj; } |
重新写一遍,加入判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
function scale(r,width){ for(var p=1;p<=60;p++) { var o=p*6; t=a+r*Math.cos(Math.PI/180*o); u=b+r*Math.sin(Math.PI/180*o); if(isInteger(o/5)){ ctx.lineWidth=2*width; v=a+(r-12)*Math.cos(Math.PI/180*o); c=b+(r-12)*Math.sin(Math.PI/180*o); } else { ctx.lineWidth=width; v=a+(r-6)*Math.cos(Math.PI/180*o); c=b+(r-6)*Math.sin(Math.PI/180*o); } ctx.beginPath(); ctx.strokeStyle="#000000"; ctx.lineCap="round"; ctx.moveTo(t,u); ctx.lineTo(v,c); ctx.stroke(); ctx.closePath(); } } scale(131,1); |
四、绘制数字
每隔5个刻度依次输出数字0-12,输出字符的坐标在对应的圆上,数字的坐标计算方法和画刻度差不多,这里就不多讲了,需要注意的一点是与刻度0对应的点坐标在三点钟位置,与实际不符,这里计算时要先把刻度值减掉15,让0刻度对应点坐标处于12点方向。
clocknumber(半径,颜色,字体样式);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function clocknumber(r,color,fontstyle){ ctx.shadowOffsetX=0; ctx.shadowOffsetY=0; ctx.shadowBlur=0; ctx.font = fontstyle; ctx.textAlign="center"; ctx.textBaseline="center"; ctx.beginPath(); for(var p=1;p<=60;p++) { var o = (p-15)*6; // 注意这里 t=a+r*Math.cos(Math.PI/180*o); u=b+r*Math.sin(Math.PI/180*o); if(isInteger(p/5)){ ctx.fillStyle=color; var num = ""+p/5+""; ctx.fillText(num,t,u+9); } ctx.closePath(); } } clocknumber(102,"#222","24px Arial bold"); |
五、绘制表针
表针是圆上某点到圆形的连线,也和刻度的画法差不多,这里先定义一个绘制从圆心到某点的线段的函数
strokeline(半径,宽度,颜色,角度);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function strokeline(r,width,color,angle){ var t,u; ctx.lineCap="round"; ctx.strokeStyle = color; ctx.lineWidth = width; ctx.shadowColor="rgba(0,0,0,0.6)"; ctx.shadowOffsetX=2; ctx.shadowOffsetY=2; ctx.shadowBlur=3; ctx.beginPath(); ctx.moveTo(a,b); t=a+r*Math.cos(Math.PI/180*angle); u=b+r*Math.sin(Math.PI/180*angle); ctx.lineTo(t,u); ctx.stroke(); ctx.closePath(); } |
再定义绘制指针的函数,这里注意时分秒都要换算成小数,如果:1分30秒换算为1.5分钟。
hour(小时,分钟,长度);
minute(分钟,秒,长度);
second(秒,长度);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function hour(h,m,l){ // 时针 h = h*5+(m/60)*5-15; var angle = h*6; strokeline(l,10,"#222222",angle); // 我在指针上又绘制一条细的白色线,做出镂空效果 strokeline(l-2,2,"#ffffff",angle); } function minute(m,s,l){ // 分针 m = m+s/60-15; var angle = m*6; strokeline(l,8,"#222222",angle); strokeline(l-2,2,"#ffffff",angle); } function second(s,l){ // 秒针 var angle = (s-15)*6; strokeline(l,2,"red",angle); } |
定义获取当前时间、日期、星期的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function getTime(){ var myDate = new Date(); var myDay = myDate.getDate() var myWeek = myDate.getDay(); var timeSec = myDate.getSeconds(); var timeMinu = myDate.getMinutes(); var timeHour = myDate.getHours(); var Millsec = myDate.getMilliseconds()/1000; var arr_week = new Array("SUN","MON", "TUE", "WED", "THU", "FRI", "SAT"); myDate = { "myDay":myDay, "myWeek":arr_week[myWeek], "timeSec":timeSec, "timeMinu":timeMinu, "timeHour":timeHour, "Millsec":Millsec }; return myDate; } |
绘制表针:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var s = getTime().timeSec; var m = getTime().timeMinu; var h = getTime().timeHour; var ms = getTime().Millsec; fillcircle(a,b,10,"#999"); // 表针孔 hour(h,m,70); // 时针 minute(m,s,92); // 分针 second(s+ms,120); // 秒针 //因为秒针后面要长出一节,所以我在对位再画一条短的 second(s+ms+30,18); // 一些细节 fillcircle(a,b,5,"red"); fillcircle(a,b,3,"#e2d8a1"); |
六、绘制日历
日历由两部分组成,若干个矩形和星期及日期的文字,这里先定义绘制矩形和文字的函数
矩形:fillerect(x,y,a,b,宽度,颜色);
1 2 3 4 5 6 7 |
function fillerect(x,y,a,b,width,color){ ctx.beginPath(); ctx.fillStyle=color; ctx.lineWidth=width; ctx.fillRect(x,y,a,b); ctx.closePath(); } |
文字:fillText(x,y,颜色,样式,文本内容,对齐方式);
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function fillText(x,y,fontcolor,fontstyle,text,align){ ctx.beginPath(); ctx.shadowOffsetX=0; ctx.shadowOffsetY=0; ctx.shadowBlur=0; ctx.fillStyle=fontcolor; ctx.font=fontstyle; ctx.textAlign="" + align + ""; ctx.textBaseline="center"; text = ""+text+""; ctx.fillText(text,x,y); ctx.closePath(); } |
绘制日历:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var myweek = getTime().myWeek; var myday = getTime().myDay; fillerect(208,167,52,18,1,"#000"); fillerect(209,168,50,16,1,"#f1f1f1"); fillerect(241,167,1,17,1,"#222"); // 这里增加一个判断,如果是周日,则输出红色字 if (myweek=="SUN"){ var weekcolor = "red"; } else { var weekcolor = "#222"; } fillText(225,181,weekcolor,"14px Tahoma bold",myweek,"center"); fillText(251,181,"#222","14px Tahoma bold",myday,"center"); |
7、添加一些别的文字
这里可以自由发挥,用刚才定义的绘制文字函数即可,这里要注意下绘制的顺序,不要遮挡到其它元素。
1 2 |
fillText(175,123,"#222","18px Georgia bold","CANVAS","center"); fillText(175,236,"#222","10px Tahoma bold","WWW.IHTMLCSS.COM","center"); |
8.让时钟动起来!
到这一步,时钟的外观已经完成,但是是不会动的,需要每隔一段时间获取下当前时间并重新绘制图形,我们把刚才所有绘制图形的操作放在一个函数里,然后每隔一段时间执行一次这个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
function clock(){ var s = getTime().timeSec; var m = getTime().timeMinu; var h = getTime().timeHour; var ms = getTime().Millsec; var myweek = getTime().myWeek; var myday = getTime().myDay; ctx.clearRect(0,0, canvas.width, canvas.height);//清除画布 grdcircle(a,b,150,"#333","#000"); grdcircle(a,b,144,"#999","#ccc"); grdcircle(a,b,134,"#666","#999"); grdcircle(a,b,133,"#fff","#dedede"); fillcircle(a,b,10,"#999"); scale(131,1); clocknumber(102,"#222","24px Arial bold"); fillText(175,123,"#222","18px Georgia bold","CANVAS","center"); fillText(175,236,"#222","10px Tahoma bold","WWW.IHTMLCSS.COM","center"); fillerect(208,167,52,18,1,"#000"); fillerect(209,168,50,16,1,"#f1f1f1"); fillerect(241,167,1,17,1,"#222"); if (myweek=="SUN"){ var weekcolor = "red"; } else { var weekcolor = "#222"; } fillText(225,181,weekcolor,"14px Tahoma bold",myweek,"center"); fillText(251,181,"#222","14px Tahoma bold",myday,"center"); hour(h,m,70); minute(m,s,92); second(s+ms,120); second(s+ms+30,18); fillcircle(a,b,5,"red"); fillcircle(a,b,3,"#e2d8a1"); } |
用setInterval每隔200毫秒执行一次:
1 |
self.setInterval(clock,200); |
到这里这个canvas的钟表就已经绘制完成,可以动起来了!
ps:我又增加了一个缩放函数,可以将画布缩放至指定宽度:
1 2 3 4 5 6 |
function CanvasScale(n){ n = n/can.width; can.width=can.width*n; can.height=can.height*n; ctx.scale(n,n); } |
将画布缩放至600
1 |
CanvasScale(600); |
文章评论 暂无评论
暂无评论