难点在于:
秒钟刻度和时钟刻度的绘制
整点文字沿着内边圆形环绕
其中刻度的环绕并不难计算,文字的环绕就比较坑爹了,canvas绘制的文字是在绘制坐标之上的(文字基线和对齐方式影响),需要进行偏移的计算,使之文字中点正好落在圆上。
这一步相当淡疼,由于api中并没有测量字高的办法,而使用fontsize,其实也并不是字的准确高度,因此,y坐标+二分之一行高向下偏移,使之垂直居中,却总是不够准确的。而x坐标+二分之一行宽向左偏移,使之水平居中,则没有这个问题,因为api提供了测量行宽的方法。
一切都是因为 ctx.measuretext(text).width 存在,但 ctx.measuretext(numtext).height 不存在。打印测量结果,也只有一个宽度属性。文档中说canvas对于绘制文字的支持比较弱,从这一点上看 何止是弱。
直接设置基线和对齐方式为居中,似乎也存在一定误差,看起来总不是那么赏心悦目。下面的代码中两种方式都写了。
会走的时钟预览:
时间显示可能略有误差。
知识点和求解参考图主要知识点为圆的坐标公式,和三角函数sin,cos计算。实际上,圆的坐标公式使用的并不多,引入求值反而可能复杂化。
下图是计算刻度线坐标和整点文字绘制坐标的参考图:
canvas画时钟效果的代码编写下面是全部代码:
<!doctype html><html lang="en"><head> <meta charset="utf-8"> <meta name="viewport" content="width=1024, height=768,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> <title>时钟</title></head><body><p style="margin: 50px"> <canvas width="300" height="300"></canvas> <canvas width="200" height="200" style="background-color: #bbbbbb"></canvas> <canvas width="200" height="200"></canvas> <canvas width="200" height="200"></canvas></p><script> var clockhelper = function (canvas, config) { if (!config) { config = {} } var ctx = canvas.getcontext('2d'); var decolor = config.decolor ? config.decolor : '#333333'; var deconfig = { ringwidth: config.ringwidth ? config.ringwidth : 6,//圆环宽度 ringcolor: config.ringcolor ? config.ringcolor : decolor,//圆环颜色 hsalel: config.hsalel ? config.hsalel : 8,//时刻度线长 hscalewidth: config.hscalewidth ? config.hscalewidth : 4,//时刻度线宽 hscalecolor: config.hscalecolor ? config.hscalecolor : decolor,//时刻度颜色 mssalel: config.mssalel ? config.mssalel : 4,//分秒刻度线长 msscalewidth: config.msscalewidth ? config.msscalewidth : 2,//分秒刻度线宽 msscalecolor: decolor,//分秒刻度颜色 hfontsize: config.hfontsize ? config.hfontsize : 18,//整点文字大小 hhandwidth: config.hhandwidth ? config.hhandwidth : 10,//时针宽度 mhandwidth: config.mhandwidth ? config.mhandwidth : 5,//分针宽度 shandwidth: config.shandwidth ? config.shandwidth : 2,//秒针宽度 hhandcolor: config.hhandcolor ? config.hhandcolor : decolor,//时针颜色 mhandcolor: config.mhandcolor ? config.mhandcolor : decolor,//分针颜色 shandcolor: config.shandcolor ? config.shandcolor : '#bb3333',//秒针颜色 handmode: ['ms', 's'].indexof("" + config.handmode) !== -1 ? config.handmode : 's',//指针读秒模式,ms:毫秒,s:秒。 clockfacecolor: config.clockfacecolor ? config.clockfacecolor : '', }; var ox = canvas.width / 2; var oy = canvas.height / 2; var width = canvas.width; var height = canvas.height; ctx.font = deconfig.hfontsize + "px 黑体"; //中线圆环半径 var ringr = (width < height) ? (width / 2 - deconfig.ringwidth / 2) : (height / 2 - deconfig.ringwidth / 2); //内圆环半径 var ringinnerr = (width < height) ? (width / 2 - deconfig.ringwidth) : (height / 2 - deconfig.ringwidth); var timer; var timesleep = 100; var isstart = false; function start() { if (isstart) { return; } isstart = true; if (deconfig.handmode == 'ms') { timesleep = 100; } else { timesleep = 1000; } ctx.clearrect(0, 0, width, height); draw(); timer = setinterval(function () { if (isstart) { ctx.clearrect(0, 0, width, height); draw(); } }, timesleep); } function stop() { isstart = false; clearinterval(timer) } function draw() { beforedraw(); drawcircleface(); drawhands(); afterdraw(); } function drawcircleface() { ctx.fillstyle = deconfig.ringcolor; ctx.strokestyle = deconfig.ringcolor; ctx.linewidth = deconfig.ringwidth; ctx.beginpath(); ctx.arc(ox, oy, ringr, 0, math.pi * 2); ctx.stroke(); if (deconfig.clockfacecolor) { ctx.fillstyle = deconfig.clockfacecolor; ctx.fill(); } var x1 = ox; var y1 = oy; var x2 = ox; var y2 = oy; var radin = 0; ctx.linewidth = deconfig.hscalewidth; // ctx.beginpath(); for (var i = 1; i <= 60; i++) { radin = i * 6 * math.pi / 180; x1 = ox + ringinnerr * math.sin(radin); y1 = oy - ringinnerr * math.cos(radin); if (i % 5 === 0) { ctx.linewidth = deconfig.hscalewidth; x2 = ox + (ringinnerr - deconfig.hsalel) * math.sin(radin); y2 = oy - (ringinnerr - deconfig.hsalel) * math.cos(radin); ctx.fillstyle = deconfig.hscalecolor; var numtext = i / 5 + ""; var textwidth = ctx.measuretext(numtext).width; var x3 = ox + (ringinnerr - deconfig.hsalel - deconfig.hfontsize) * math.sin(radin); var y3 = oy - (ringinnerr - deconfig.hsalel - deconfig.hfontsize) * math.cos(radin); ctx.textalign = 'center'; ctx.textbaseline = 'middle'; //不设置文字居中,基线居中,自己计算。貌似都有误差。因为旋转过程中,角度变化,且文字宽高不尽相同 // var x3 = ox + (ringinnerr - deconfig.hsalel - deconfig.hfontsize) * math.sin(radin) - textwidth / 2; // var y3 = oy - (ringinnerr - deconfig.hsalel - deconfig.hfontsize) * math.cos(radin) + deconfig.hfontsize/ 2; //x2,y2已经求过,化简为: // var x3 = x2 - deconfig.hfontsize * math.sin(radin) - textwidth / 2; // var y3 = y2 + deconfig.hfontsize * math.cos(radin) + textwidth / 2; //文字x轴向左偏移一半文字宽,使之水平居中;向下偏移一半高度,使之垂直居中。 // 实际中发现,字高没法测(api无),而使用fontsize不准。但y轴加上字宽,位置倒是更对齐一些。 // var x3 = x2 + textwidth / 2; // var y3 = y2 - deconfig.hfontsize / 2; ctx.filltext(numtext, x3, y3); } else { ctx.linewidth = deconfig.msscalewidth; x2 = ox + (ringinnerr - deconfig.mssalel) * math.sin(radin); y2 = oy - (ringinnerr - deconfig.mssalel) * math.cos(radin); } ctx.beginpath(); ctx.moveto(x1, y1); ctx.lineto(x2, y2); ctx.stroke(); } } //改变坐标中点,并旋转画布也许是更好的选择。 function drawhands() { var date = new date(); var h = date.gethours() % 12; var m = date.getminutes(); var s = date.getseconds(); var ms = date.getmilliseconds(); // console.log(h + ":" + m + ":" + s); // 时针 var hradin = (h + m / 60 + s / 3600) * math.pi * 2 / 12; var mradin = (m + s / 60) * math.pi * 2 / 60; var sradin; if (deconfig.handmode == 'ms') { sradin = (s + ms / 1000) * math.pi * 2 / 60; } else { sradin = s * math.pi * 2 / 60; } var x = 0; var y = 0; var hdotr = deconfig.hhandwidth + 2; var mdotr = 0.6 * hdotr var sdotr = 0.5 * hdotr //秒针半径 var shandr = ringinnerr - deconfig.hsalel * 2 //分针半径 var mhandr = 0.8 * shandr; //时针半径 var hhandr = 0.7 * mhandr; //时针 ctx.beginpath(); ctx.linewidth = deconfig.hhandwidth; ctx.strokestyle = deconfig.hhandcolor; ctx.strokestyle = deconfig.hhandcolor; ctx.moveto(ox, oy); x = ox + hhandr * math.cos(hradin - math.pi / 2); y = oy + hhandr * math.sin(hradin - math.pi / 2); ctx.lineto(x, y); ctx.stroke(); //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。) ctx.beginpath(); ctx.linewidth = 0; ctx.arc(x, y, deconfig.hhandwidth / 2, 0, 2 * math.pi); ctx.fill(); //中心 ctx.beginpath(); // ctx.linewidth = hdotr; ctx.arc(ox, oy, hdotr / 2, 0, math.pi * 2); ctx.fill(); ctx.stroke(); //分针 ctx.beginpath(); ctx.linewidth = deconfig.mhandwidth; ctx.strokestyle = deconfig.mhandcolor; ctx.fillstyle = deconfig.mhandcolor; ctx.moveto(ox, oy); x = ox + mhandr * math.cos(mradin - math.pi / 2); y = oy + mhandr * math.sin(mradin - math.pi / 2); ctx.lineto(x, y); ctx.stroke(); //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。) ctx.beginpath(); ctx.linewidth = 0; ctx.arc(x, y, deconfig.mhandwidth / 2, 0, 2 * math.pi); ctx.fill(); //中心 ctx.beginpath(); ctx.arc(ox, oy, mdotr / 2, 0, math.pi * 2); ctx.stroke(); //秒针 ctx.beginpath(); ctx.strokestyle = deconfig.shandcolor; ctx.fillstyle = deconfig.shandcolor; ctx.linewidth = deconfig.shandwidth; //秒针有长短两线 x = ox - shandr / 4 * math.cos(sradin - math.pi / 2); y = oy - shandr / 4 * math.sin(sradin - math.pi / 2); ctx.moveto(x, y); x = ox + shandr * math.cos(sradin - math.pi / 2); y = oy + shandr * math.sin(sradin - math.pi / 2); ctx.lineto(x, y); ctx.stroke(); //针尖。直接圆型了(矩形指针来绘制针尖,计算复杂。。。) ctx.beginpath(); ctx.linewidth = 0; ctx.arc(x, y, deconfig.shandwidth / 2, 0, 2 * math.pi); ctx.fill(); //中心 ctx.beginpath(); ctx.fillstyle = decolor; ctx.arc(ox, oy, sdotr, 0, math.pi * 2); ctx.fill(); ctx.stroke(); } function beforedraw() { if (typeof exp.beforedraw === 'function') { exp.beforedraw(ctx, deconfig); } } function afterdraw() { if (typeof exp.afterdraw === 'function') { exp.afterdraw(ctx, deconfig); } } var exp = { start: start, stop: stop, beforedraw: null, afterdraw: null, } return exp; } var clockcanvas1 = document.getelementsbytagname('canvas')[0]; var clockcanvas2 = document.getelementsbytagname('canvas')[1]; var clockcanvas3 = document.getelementsbytagname('canvas')[2]; var clockcanvas4 = document.getelementsbytagname('canvas')[3]; var clock = clockhelper(clockcanvas1, {mhandcolor: '#3333bb', shandcolor: '#bb3333'}); clock.start(); settimeout(function () { clock.stop() }, 5000) settimeout(function () { clock.start(); }, 8000) clockhelper(clockcanvas2, { mhandcolor: 'green', hhandwidth: 6, mhandwidth: 4, hfontsize: 14, hscalewidth: 2, handmode: 'ms' }).start(); clockhelper(clockcanvas2, { mhandcolor: 'green', hhandwidth: 6, mhandwidth: 4, hfontsize: 14, hscalewidth: 2, handmode: 'ms' }).start(); clockhelper(clockcanvas3, { decolor: '#bbbbbb', shandcolor: '#bbbbbb', clockfacecolor: '#112233',//钟面 hhandwidth: 6, mhandwidth: 4, hfontsize: 14, hscalewidth: 2, handmode: 's' }).start(); var clock4 = clockhelper(clockcanvas4, { decolor: '#bbbbbb', shandcolor: '#bbbbbb', // clockfacecolor: '#112233', hhandwidth: 6, mhandwidth: 4, hfontsize: 14, hscalewidth: 2, handmode: 's' }); clock4.afterdraw = function (ctx, deconfig) { var grd = ctx.createlineargradient(0, 0, clockcanvas4.width, clockcanvas4.height); grd.addcolorstop(0, "rgba(255,0,0,0.3)"); grd.addcolorstop(1, "rgba(0,0,255,0.5)"); ctx.fillstyle = grd; ctx.arc(clockcanvas4.width/2,clockcanvas4.height/2,clockcanvas4.width/2,0,math.pi*2); // ctx.fillrect(0, 0, clockcanvas4.width, clockcanvas4.height); ctx.fill(); ctx.filltext('时钟绘制完成后,自定义其他绘制',clockcanvas4.width/2,clockcanvas4.height - deconfig.hfontsize); }; clock4.start();</script></body></html>
说明:
1、clockhelper第一个参数传入画布。第二个参数传入时钟界面的配置对象,包括指针、刻度的颜色、大小等,配置项和clockhelper中的deconfig默认对象是相对的,参考deconfig的属性传入参数即可。
2、clockhelper的封装性略差,仅是基本能用型。但属性不多,改造应该并不困难。
3、提供了时钟界面绘制之前和之后的方法,可以在beforedraw和afterdraw这两个方法中执行自己的逻辑。但是由于事先设计没有留出足够的空白像素,用处不大。只能进行一些简单的再绘制。比如给钟面添加色彩、渐变。
相关推荐:
h5+c3实现时钟效果
使用canvas制作时钟动画的方法
以上就是使用h5 canvas实现时钟的动态效果的详细内容。
