做一个canvas时钟(四)
本帖最后由 马黑黑 于 2024-3-25 10:55 编辑 <br /><br /><style>.mama { font: normal 18px / 26px sans-serif; }
.mama p { margin: 12px 0; }
.mama mark { padding: 0 6px; background: lightblue; }
.wrap { margin: 20px auto 0; text-align: center; }
#canv, #canv1 { border: 1px solid gray; }
.mum { position: relative; margin: 0; padding: 10px; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; color: black; background: rgba(240, 240, 240,.95); box-shadow: 2px 2px 4px gray; border: thick groove lightblue; border-radius: 6px; }
.mum ::selection { background-color: rgba(0,100,100,.35); }
.mum div { margin: 0; padding: 0; }
.mum cl-cd { display: block; position: relative; margin: 0 0 0 50px; padding: 0 0 0 10px; white-space: pre-wrap; overflow-wrap: break-word; border-left: 1px solid silver; }
.mum cl-cd::before { position: absolute; content: attr(data-idx); width: 50px; color: gray; text-align: right; transform: translate(-70px); }
.tRed { color: red; }
.tBlue { color: blue; }
.tGreen { color: green; }
.tDarkRed { color: darkred; }
.tMagenta { color: magenta; }
</style>
<div class="mama">
<p>到第三讲结束时canvas时钟已经有了三根指针,它们可能想有个家,我们应该让它们安家落户了——给它们绘制钟壳和钟盘,同时给指针做一个指针扣,以免将来时钟运行时指针在走动的时候指针散架掉落。</p>
<p>这就是绘制圆和圆环。在canvas画布上画圆有两种方法,一种是 arc() 方法,另一种是 arcTo() 方法,它们实际上都不是画圆,而是画弧线,用它们画圆无非就是画一个头尾彼此衔接的弧线。arcTo() 略显复杂,我们选择 arc() 方法来完成时钟的外壳(含时钟钟盘,当代工业一体化的构造)、指针扣以及将来会加上的圆点刻度等绘制工作。arc() 画弧形需要 6 个参数,多是多了点但不要方,下面的代码有详细说明:</p>
<div class='mum'>
<div class="tGreen"><cl-cd data-idx="1">/* arc() 语法:</cl-cd>
<cl-cd data-idx="2"> </cl-cd>
<cl-cd data-idx="3"> <span class="tRed">ctx.arc(x, y, r, startAngle, endAngle, anticlockwise);</span></cl-cd>
<cl-cd data-idx="4"> </cl-cd>
<cl-cd data-idx="5"> 参数详解:</cl-cd>
<cl-cd data-idx="6"> </cl-cd>
<cl-cd data-idx="7"> x,y - 圆心XY坐标值</cl-cd>
<cl-cd data-idx="8"> r - 圆的半径</cl-cd>
<cl-cd data-idx="9"> startAngle - 绘制弧线起始点弧度</cl-cd>
<cl-cd data-idx="10"> endAngle - 绘制弧线结束点弧度</cl-cd>
<cl-cd data-idx="11"> anticlockwise - 绘制弧线的方向,true = 逆时针绘制, false = 顺时针绘制(默认)</cl-cd>
<cl-cd data-idx="12">*/</cl-cd></div>
<cl-cd data-idx="13"> </cl-cd>
<div class="tGreen"><cl-cd data-idx="14">/* 例:以 150,150 为圆心,半径 140 画一个圆</cl-cd>
<cl-cd data-idx="15"> arc() 方法描述的是绘制路径,并没有填充和描边功能,所以描述之后:</cl-cd>
<cl-cd data-idx="16"> ① 用 fill() 方法绘制实心圆</cl-cd>
<cl-cd data-idx="17"> ② 用 stroke() 方法绘制圆环</cl-cd>
<cl-cd data-idx="18"> 凡绘制与路径相关的图形,均需要开启路径 <span class="tRed">beginPath()</span> 指令,表示所绘制的图形路径与之前可能存在的任何路径<span class="tRed">无关</span></cl-cd>
<cl-cd data-idx="19">*/</cl-cd></div>
<cl-cd data-idx="20">ctx.fillStyle = <span class="tMagenta">'tan'</span>; <span class="tGreen">//填充色</span></cl-cd>
<cl-cd data-idx="21">ctx.strokeStyle = <span class="tMagenta">'darkgreen'</span>; <span class="tGreen">//描边色</span></cl-cd>
<cl-cd data-idx="22">ctx.beginPath(); <span class="tGreen">//开启新路径</span></cl-cd>
<cl-cd data-idx="23">ctx.arc(150, 150, 140, 0, <span class="tRed">Math</span>.PI * 2, false); <span class="tGreen">//描述圆的路径,其中Math.PI是弧度,后面解释 </span></cl-cd>
<cl-cd data-idx="24">ctx.fill(); <span class="tGreen">//给上述路径填充颜色</span></cl-cd>
<cl-cd data-idx="25">ctx.stroke(); <span class="tGreen">//给上述路径描边</span></cl-cd>
</div>
<p>靓丽的样纸如下所示:</p>
<div class="wrap"><canvas id="canv" width="300" height="300"></canvas></div>
<p>上例代码中,从 0 弧度开始画圆,就是从三点钟方向开始,到圆的终点不论是顺时针还是逆时针绘制都是绕一圈后回到原点,360度。360度要换算成弧度,弧度和角度的关系以及360度转弧度的推演如下:</p>
<blockquote>弧度 = Math.PI / 180 * 角度 <br>转一圈的弧度 = Math.PI / 180 * 360 → 转一圈的弧度 = Math.PI * 2</blockquote>
<p>Math.PI 就是数学中的派,π,圆周率。下来我们将画圆的 arc() 方法也封装一下,方面我们将来画多个圆形图案:</p>
<div class='mum'>
<div class="tGreen"><cl-cd data-idx="1">/* 函数 :画圆</cl-cd>
<cl-cd data-idx="2"> x,y,r - 圆心XY坐标坐标和半径r</cl-cd>
<cl-cd data-idx="3"> lw - lineWidth,描边线宽</cl-cd>
<cl-cd data-idx="4"> color1,color2 - 填充色和描边色</cl-cd>
<cl-cd data-idx="5">*/</cl-cd></div>
<cl-cd data-idx="6"><span class="tBlue">let</span> draw_circle = (x,y,r,lw,color1,color2) => {</cl-cd>
<cl-cd data-idx="7"> ctx.save();</cl-cd>
<cl-cd data-idx="8"> ctx.fillStyle = color1;</cl-cd>
<cl-cd data-idx="9"> ctx.strokeStyle = color2;</cl-cd>
<cl-cd data-idx="10"> ctx.lineWidth = lw;</cl-cd>
<cl-cd data-idx="11"><span class="tRed"> ctx.beginPath();</span></cl-cd>
<cl-cd data-idx="12"> ctx.arc(x,y,r,0,<span class="tRed">Math</span>.PI * 2);</cl-cd>
<cl-cd data-idx="13"> ctx.fill();</cl-cd>
<cl-cd data-idx="14"> ctx.stroke();</cl-cd>
<cl-cd data-idx="15"> ctx.restore();</cl-cd>
<cl-cd data-idx="16">};</cl-cd>
<cl-cd data-idx="17"> </cl-cd>
<cl-cd data-idx="18"><span class="tGreen">//函数调用举例:</span></cl-cd>
<cl-cd data-idx="19">draw_circle(150,150,140,10,<span class="tMagenta">'pink'</span>,<span class="tMagenta">'red'</span>);</cl-cd>
</div>
<p>画圆无需转换画布的坐标系,但仍然在函数中使用了 save() 和 reatore() 方法,因为ctx的填充色、描边色和线宽等也是画布的设置状态,保存和复原画布状态能让我们的整体绘制工作中的每一个环节换不干扰。<span class="tRed">beginPath()</span> 方法除了 fillRect() 和 strokeRect() 这两个画矩形的不需要,画圆等都少不了它,切记。</p>
<p>最后,我们将上一讲的指针搬过来,看看效果如何:</p>
<div class="wrap"><canvas id="canv1" width="300" height="300"></canvas></div>
<p>嗯,现在指针可以欢歌了:我想要有个家,一个哪怕是空荡荡的地方……</p>
<p>以上效果的实现代码:</p>
<div class='mum'>
<cl-cd data-idx="1"><<span class="tDarkRed">canvas</span> <span class="tRed">id</span>=<span class="tMagenta">"canv"</span> width=<span class="tMagenta">"300"</span> height=<span class="tMagenta">"300"</span>><<span class="tDarkRed">/canvas</span>><<span class="tDarkRed">/div</span>></cl-cd>
<cl-cd data-idx="2"> </cl-cd>
<cl-cd data-idx="3"><<span class="tDarkRed">script</span>></cl-cd>
<cl-cd data-idx="4"> </cl-cd>
<cl-cd data-idx="5"><span class="tBlue">let</span> ctx = canv.getContext(<span class="tMagenta">'2d'</span>); <span class="tGreen">//获取画笔</span></cl-cd>
<cl-cd data-idx="6"> </cl-cd>
<cl-cd data-idx="7"><span class="tGreen">// 函数 :画指针</span></cl-cd>
<cl-cd data-idx="8"><span class="tBlue">let</span> draw_rect = (x, y, w, h, rad, color) => {</cl-cd>
<cl-cd data-idx="9"> ctx.save();</cl-cd>
<cl-cd data-idx="10"> ctx.fillStyle = color;</cl-cd>
<cl-cd data-idx="11"> ctx.translate(150,150);</cl-cd>
<cl-cd data-idx="12"> ctx.rotate(rad);</cl-cd>
<cl-cd data-idx="13"> ctx.fillRect(x,y,w,h);</cl-cd>
<cl-cd data-idx="14"> ctx.restore();</cl-cd>
<cl-cd data-idx="15">};</cl-cd>
<cl-cd data-idx="16"> </cl-cd>
<cl-cd data-idx="17"><span class="tGreen">// 函数 :画圆</span></cl-cd>
<cl-cd data-idx="18"><span class="tBlue">let</span> draw_circle = (x,y,r,lw,color1,color2) => {</cl-cd>
<cl-cd data-idx="19"> ctx.save();</cl-cd>
<cl-cd data-idx="20"> ctx.fillStyle = color1;</cl-cd>
<cl-cd data-idx="21"> ctx.strokeStyle = color2;</cl-cd>
<cl-cd data-idx="22"> ctx.lineWidth = lw;</cl-cd>
<cl-cd data-idx="23"> ctx.beginPath();</cl-cd>
<cl-cd data-idx="24"> ctx.arc(x,y,r,0,<span class="tRed">Math</span>.PI * 2);</cl-cd>
<cl-cd data-idx="25"> ctx.fill();</cl-cd>
<cl-cd data-idx="26"> ctx.stroke();</cl-cd>
<cl-cd data-idx="27"> ctx.restore();</cl-cd>
<cl-cd data-idx="28">};</cl-cd>
<cl-cd data-idx="29"> </cl-cd>
<cl-cd data-idx="30"><span class="tGreen">///下面画各时钟元素,注意顺序</span></cl-cd>
<cl-cd data-idx="31">draw_circle(150,150,140,10,<span class="tMagenta">'tan'</span>,<span class="tMagenta">'darkgreen'</span>); <span class="tGreen">//钟盘</span></cl-cd>
<cl-cd data-idx="32">draw_rect(0, -5, 100, 10, 0 * Math.PI/180, <span class="tMagenta">'snow'</span>); <span class="tGreen">//时针</span></cl-cd>
<cl-cd data-idx="33">draw_rect(0, -3, 120, 6, 270 * Math.PI/180, <span class="tMagenta">'white'</span>); <span class="tGreen">//分针</span></cl-cd>
<cl-cd data-idx="34">draw_rect(0, -2, 130, 4, 240 * Math.PI/180, <span class="tMagenta">'red'</span>); <span class="tGreen">//秒钟</span></cl-cd>
<cl-cd data-idx="35">draw_circle(150,150,6,6,<span class="tMagenta">'white'</span>,<span class="tMagenta">'red'</span>); <span class="tGreen">//指针扣</span></cl-cd>
<cl-cd data-idx="36"> </cl-cd>
<cl-cd data-idx="37"><<span class="tDarkRed">/script</span>></cl-cd>
</div>
<p>留意一下时针、分针和秒针的旋转参数所用的计算式子,这里先略过,在未来章节合适的地方我会对此进行解释。</p>
</div>
<script>
let ctx = canv.getContext('2d'), ctx1 = canv1.getContext('2d');
ctx.fillStyle = 'tan';
ctx.strokeStyle = 'darkgreen';
ctx.lineWidth = 10;
ctx.arc(150, 150, 140, 0, Math.PI * 2, false);
ctx.fill();
ctx.stroke();
let draw_rect = (x, y, w, h, rad, color) => {
ctx1.save();
ctx1.fillStyle = color;
ctx1.translate(150,150);
ctx1.rotate(rad);
ctx1.fillRect(x,y,w,h);
ctx1.restore();
};
let draw_circle = (x,y,r,lw,color1,color2) => {
ctx1.save();
ctx1.fillStyle = color1;
ctx1.strokeStyle = color2;
ctx1.lineWidth = lw;
ctx1.beginPath();
ctx1.arc(x,y,r,0,Math.PI * 2);
ctx1.fill();
ctx1.stroke();
ctx1.restore();
};
draw_circle(150,150,140,10,'tan','darkgreen');
draw_rect(0, -5, 100, 10, 0, 'snow');
draw_rect(0, -3, 120, 6, 270 * Math.PI/180, 'white');
draw_rect(0, -2, 130, 4, 240 * Math.PI/180, 'red');
draw_circle(150,150,6,6,'white','red');
console.log(-90 * Math.PI/180);
</script>
这个以前介入过,忘了。 老师辛苦了,不懂的人飘过 庶民 发表于 2024-3-24 08:29
这个以前介入过,忘了。
接触过后总不是用,会忘得差不多的{:4_170:} 世外桃源 发表于 2024-3-24 10:54
老师辛苦了,不懂的人飘过
{:4_190:} 这个时钟越来越接近实用真相。。{:4_173:} TAN这个颜色常用。还是挺好看滴 南无月 发表于 2024-3-24 17:41
TAN这个颜色常用。还是挺好看滴
耐看不刺眼 马黑黑 发表于 2024-3-24 17:57
耐看不刺眼
{:4_173:}彩色搭配得跟老师学学 南无月 发表于 2024-3-24 19:54
彩色搭配得跟老师学学
俺乱来的
马黑黑 发表于 2024-3-24 20:30
俺乱来的
乱得好,乱得妙 南无月 发表于 2024-3-24 20:36
乱得好,乱得妙
还乱的呱呱叫了 “凡绘制与路径相关的图形,均需要开启路径 pathBegin() 指令”
这个必须使劲记一下{:4_199:} draw_circle(150,150,6,6,'white','red'); //指针扣
眼睛可见的小白点只剩3吧,因为红色线宽是6,而线宽是从中心线往两边分的。
记得以前学习的一个内容,我好像弄不清宽度的起算从哪开始的{:4_173:} 这个讲解又是十分清楚透彻,真太好了。谢谢黑黑{:4_199:} 马黑黑 发表于 2024-3-24 21:29
还乱的呱呱叫了
押韵押得好。。。也行啊,洪钟似的响亮{:4_170:} 南无月 发表于 2024-3-25 18:06
押韵押得好。。。也行啊,洪钟似的响亮
{:4_172:} 马黑黑 发表于 2024-3-25 22:21
这个时钟整出来太漂亮了。。
{:4_173:}
效果绝佳 。。 这贴的回复肯定被外星人劫持了{:4_170:} 南无月 发表于 2024-3-28 18:23
这贴的回复肯定被外星人劫持了
有这回事?
页:
[1]