马黑PHP整站系统

做一个canvas时钟(五)

位置: 首页 > 前端三套件[ 发布时间: 2024.3.25  作者: 马黑  阅读: 324 ]

时钟少不了刻度,我们打算用小圆点来模拟刻度,每分钟一个刻度标志,让60个小圆点均匀分布在钟盘的边缘、或嵌入时钟的外壳或与外壳保持一定距离,我们选择后者。前面的课程我们编写了一个画圆的函数,我们还是用它来画这60个小圆点,不同的是,我们还需要将这些小圆点安排到钟盘的边缘。这需要一些算法,辅之以一个 for 循环语句便可完成:

/* 绘制 60 个小圆点刻度 */ for(let i = 0; i < 60; i ++) {     let radian = Math.PI/180*(i*6);     let x = 150 + 130 * Math.cos(radian), y = 150 + 130 * Math.sin(radian);     draw_circle(x,y,1,2,'gray','gray'); }

解释一下代码:for(...) {...} 是一个循环语句。先理解小括号里的内容:i 是一个循环次序变量,它从 0 开始;在它 < 60 这个条件下,花括号里的语句都将得到执行;i ++ 表示 i 是一个自增变量,每循环完一次自己 + 1,这样 i < 60 才会有结束的时候,要不然for语句就是死循环了。再来理解花括号里的语句,首先我们声明一个变量 radian,弧度变量,这个变量的赋值是计算当下准备绘制的圆点相对于它所在坐标系中心的弧度,这个圆点偏移坐标系原点(0,0)一定角度,我们需要将角度变为弧度,之前的章节提到过,从角度中求弧度的公式是Math.PI/180*角度,那么,第 i 个圆点就是第三行代码所表示的,即,Math.PI/180*(i*6);,其中 i*6 不必放在括号里,这里这么放是为了让大家明白:60个圆点均匀分布在圆周上时两个圆点间的平均偏移量是 6 度,在for循环中第 i 个圆点的角度乘以 i 才是它偏移坐标系原点的角度;其次,求小圆点在画布上的圆心XY坐标值。有了弧度,我们就可以利用三角函数公式算出圆点在圆周上的左上角XY坐标的xy值,公式为x = r*cos(弧度), y = r*sin(弧度),其中 r 为圆的半径(圆指小圆点分布于其圆周上的那个大圆,这里可以看成是钟盘,不是指小圆点的半径)。这个用 JS 来表示,就是第四行代码let x = 150 + 130 * Math.cos(radian), y = 150 + 130 * Math.sin(radian);。这里为什么加150呢?因为我们画圆的函数使用HTML坐标系,没有对画布做坐标系移位,其原点位于画布的左上角(0,0),画出的小圆点只有四分之一显示在画布里,所以小圆点的圆心xy坐标位置要加上画布宽、高各一半才能推送到我们期望的钟盘的圆周上;准备就绪,接着可以真正绘制小圆点了,第五行代码,draw_circle(x,y,1,2,'gray','gray');,其中:xy参数是刚刚计算出来的第 i 个小圆点的左上角坐标值,1 是小圆点的半径,2 是小圆点的边框线宽,后两个 'gray' 是填充色和描边色,这样画出来的小圆点其实就是直径为6像素的实心圆。效果如下:

指针的颜色和前面的有所不同,事实上,一些细节也相应做了调整,毕竟时钟的部件越来越多,彼此间需要相互照应,打磨过程中修改一些细节内容不可避免,这里就不一一说明了。

现在,需要给钟盘加上钟点标志,1~12的数字。这先得弄懂canvas画布怎样绘制文本,使用 drawText() 方法,语法如下:

ctx.drawText(text,x,y);

其中,参数 text 是要绘制的文本,x,y 是起始位置。绘制文本还涉及到文本样式的设置,ctx.font = 'bold 18px "宋体"';,采用的是CSS的 font 属性设置方法;文本的水平方向对齐方式,ctx.textAlign = 'center';,还是CSS方法,表示水平方向居中对齐;文本垂直方向对齐指的是基于文本基线,文本基线就像我们在这里见到的每一行文本一样都有一条虚拟的基线(默认在文本的底部),ctx.textBaseline = 'middle';,表示文本在垂直方向基于文本基线居中对齐,换句话说就是文本基线穿过每一个字的中心,这个设置对时钟的钟点标志有帮助,因为时钟钟点数字是环绕布局的;还有文本的填充颜色,ctx.fillStyle = 'red';,文本描边颜色,ctx.strokeStyle = 'green';最后,文本若是以填充方式绘制,就用 ctx.fill();方法,若是以描边方式绘制就用ctx.stroke();方法绘制,假若既要描边又要填充就两种方法都用。必须注意,如果使用 stroke() 方法绘制文本会涉及到新路径的开辟,所以要在 stroke() 前先 ctx.beginPath();,以免被前面绘制操作中可能存在的路径干扰,fill() 方法没有这个要求。

我们把文本绘制 drawText() 也封装成自定义函数,先看代码:

/* 函数 :绘制文本 */ let draw_text = (txt,x,y,color,fontsize=18,b="bold") => {     ctx.save(); /* 保存画布状态 */     ctx.translate(150,150); /* 画布坐标系移动到画布的中心 */     ctx.font = `${b} ${fontsize}px sans-serif`; /* 设置字体 */     ctx.textAlign = 'center'; /* 文本水平居中 */     ctx.textBaseline="middle"; /* 文本垂直方向在文本基线的中间 */     ctx.fillStyle = color; /* 文本颜色(其实是画笔颜色) */     ctx.fillText(txt,x,y); /* 填充文本 */     ctx.restore(); };

函数参数较多,所幸都遵循原始绘制函数的参数需求且其他参数都是语义化,并不难理解,其中,text 为要绘制的文本,x,y 为 文本绘制于何处的XY坐标,fontsize 和 b 两个参数可选,调用者不传值时使用函数定义的值,fontsize 是字体大小,默认等于18px,b 是粗体与否,默认粗体 bold。用这个函数绘制12个钟点标志,方法和绘制刻度小圆点基本相同,只是不加上画布坐标系的半径,因为函数已经转换了坐标系。可以在后面提供的代码体会。

以下是加上钟点数字标志之后的时钟效果,还顺带加上了产品的 Logo 以避免可能发生的不必要的产权纠纷。画布的边框我们不再设置,脚手架是时候撤走了:

最后给出上述效果的完整代码:

<style>     .wrap { margin-top: 20px; text-align: center; } </style>   <div class="wrap"><canvas id="canv" width="300" height="300"></canvas></div> <script> /* 获取画笔 */ let ctx = canv.getContext('2d');   /* 函数 :绘制矩形(指针) */ let draw_rect = (x, y, w, h, rad, color) => { ctx.save(); ctx.fillStyle = color; ctx.translate(150,150); ctx.rotate(rad); ctx.fillRect(x,y,w,h); ctx.restore(); };   /* 函数 :绘制圆(环) */ let draw_circle = (x,y,r,lw,color1,color2) => { ctx.save(); ctx.fillStyle = color1; ctx.strokeStyle = color2; ctx.lineWidth = lw; ctx.beginPath(); ctx.arc(x,y,r,0,Math.PI * 2); ctx.fill(); ctx.stroke(); ctx.restore(); };   /* 函数 :绘制文本 */ let draw_text = (txt,x,y,color,fontsize=18,b="bold") => {     ctx.save();     ctx.translate(150,150);     ctx.font = `${b} ${fontsize}px sans-serif`;     ctx.textAlign = 'center';     ctx.textBaseline="middle";     ctx.fillStyle = color;     ctx.fillText(txt,x,y);     ctx.restore(); };   /* 将所有的具体画法集中在一个函数里 */ let render = () => {     ctx.clearRect(0,0,300,300); /* 每一次绘制前都首先擦除画布(檫黑板) */     draw_circle(150,150,140,10,'tan','darkgreen'); /* 钟壳和钟面 */       /* 钟点 */     for(let i = 0; i < 12; i ++) {         let radian = Math.PI/180*(i*30-60);         let x = 115 * Math.cos(radian), y = 115 * Math.sin(radian);         draw_text(i+1, x, y, 'green');     }       /* 刻度 */     for(let i = 0; i < 60; i ++) {         let radian = Math.PI/180*(i*6);         let x = 150 + 130 * Math.cos(radian), y = 150 + 130 * Math.sin(radian);         draw_circle(x,y,1,2,'gray','gray');     }       draw_text('HUACHAO',0,60,'gray'); /* 时钟 Logo */     draw_rect(0, -3, 90, 6, 0, 'lightgreen'); /* 时针 */     draw_rect(0, -2, 100, 4, 270 * Math.PI/180, 'lightgreen'); /* 分针 */     draw_rect(0, -1, 120, 2, 240 * Math.PI/180, 'lightgreen'); /* 秒针 */     draw_circle(150,150,6,6,'white','lightgreen'); /* 指针扣 */ };   render(); </script>

前一篇: 做一个canvas时钟(四)
下一篇: 做一个canvas时钟(六)

发表评论:

       

评论列表 [2条]

#2 | 飞飞 于 2024-3-25 21:31 发布: 看完了。。老师创意一百分。。从构思到实现,太厉害了。。

#1 | 悄然 于 2024-3-25 12:43 发布: 这节课分上半段和下半段。。。看到加小圆点的部分。。加刻度部分晚上回去再看。。。烧脑啊烧脑。。

Copyright © 2023 All Right Reserved 马黑PHP文章管理整站系统v1.8
联系我们: gxblk@163.com