马黑黑 发表于 2024-5-2 10:51

canvas画布绘制正五角星

<style>
.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>

<h2>效果:</h2>
<canvas id="canv" width="400" height="400"></canvas></div>
<h2>代码:</h2>
<div class='mum'>
<cl-cd data-idx="1">&lt;<span class="tDarkRed">canvas</span> <span class="tRed">id</span>=<span class="tMagenta">"canv"</span> width=<span class="tMagenta">"400"</span> height=<span class="tMagenta">"400"</span>&gt;&lt;<span class="tDarkRed">/canvas</span>&gt;&lt;<span class="tDarkRed">/div</span>&gt;</cl-cd>
<cl-cd data-idx="2"> </cl-cd>
<cl-cd data-idx="3">&lt;<span class="tDarkRed">script</span>&gt;</cl-cd>
<cl-cd data-idx="4"><span class="tBlue">var</span> ctx = canv.getContext(<span class="tMagenta">'2d'</span>);</cl-cd>
<cl-cd data-idx="5"><span class="tBlue">var</span> points = [], keys = ;</cl-cd>
<cl-cd data-idx="6">&nbsp;</cl-cd>
<cl-cd data-idx="7"><span class="tBlue">for</span>(<span class="tBlue">let</span> i = 0; i &lt; 5; i ++) {</cl-cd>
<cl-cd data-idx="8">&nbsp; &nbsp; <span class="tBlue">var</span> x = 200 + 190 * <span class="tRed">Math</span>.cos((72*i-18) * <span class="tRed">Math</span>.PI/180),</cl-cd>
<cl-cd data-idx="9">&nbsp; &nbsp; &nbsp; &nbsp; y = 200 + 190 * <span class="tRed">Math</span>.sin((72*i - 18) * <span class="tRed">Math</span>.PI/180);</cl-cd>
<cl-cd data-idx="10">&nbsp; &nbsp; points.push({<span class="tBlue">x:</span> x, <span class="tBlue">y:</span> y});</cl-cd>
<cl-cd data-idx="11">}</cl-cd>
<cl-cd data-idx="12">&nbsp;</cl-cd>
<cl-cd data-idx="13">ctx.beginPath();</cl-cd>
<cl-cd data-idx="14">points.forEach((_,key) =&gt; ctx.lineTo(points].x, points].y));</cl-cd>
<cl-cd data-idx="15">ctx.closePath();</cl-cd>
<cl-cd data-idx="16">ctx.lineWidth = 3;</cl-cd>
<cl-cd data-idx="17">ctx.fillStyle = <span class="tMagenta">'red'</span>;</cl-cd>
<cl-cd data-idx="18">ctx.fill();</cl-cd>
<cl-cd data-idx="19">&lt;<span class="tDarkRed">/script</span>&gt;</cl-cd>
</div>

<script>

var ctx = canv.getContext('2d');
var points = [], keys = ;

for(let i = 0; i < 5; i ++) {
        var x = 200 + 190 * Math.cos((72*i-18) * Math.PI/180),
                y = 200 + 190 * Math.sin((72*i - 18) * Math.PI/180);
        points.push({x: x, y: y});
}

ctx.beginPath();
points.forEach((_,key) => ctx.lineTo(points].x, points].y));
ctx.closePath();
ctx.lineWidth = 3;
ctx.fillStyle = 'red';
ctx.fill();

</script>

马黑黑 发表于 2024-5-2 11:40

本帖最后由 马黑黑 于 2024-5-2 11:44 编辑

正五角星就是我们国旗上使用的五角星,五个角尖每个角的角度度为36度。

传统的canvas画布绘制正五角星的方法首先需要计算出五个角尖的xy坐标,再计算五个内角的xy坐标,然后按角尖→内角→角尖这样的次序将各点连接起来。这种画法需要注意一个问题:外圆和内圆的半径比例应为 5:2,否则画出的五角星不是正五角星。

一楼效果不采用这种方法。思路是酱紫:

计算出正五边形各个角的xy坐标,然后每间隔一个角将各个角连接起来。这其实是一笔手绘正五角星的方法的代码演绎,不同的是效果——代码绘制的绝对比手绘的精准。

具体画法:

先定义一个点坐标数组 points变量,用来存储即将计算的各个角的坐标值,同时设计一个数组下标序列列表 keys,用以指引绘制连线。

接着用一个for循环语句求出正五边形各个角的xy坐标,使用 x=cx+r*cos(弧度)、y=cy + r*sin(弧度) 这两个公式(cx、cy 为圆心坐标 x、y 值,r 为半径):

    x = 200 + 190 * Math.cos((72*i-18) * Math.PI/180)
    y = 200 + 190 * Math.sin((72*i-18) * Math.PI/180)

200 分别是圆心x、y坐标值,190 是圆的半径;72 来源于 360/5,就是五等分圆的角度值;i 是循环步进变量,不同的角角度不一样;每个角减去 18 是为了扶正五角星而做的调节;Math.PI/180 是角度转换为弧度的需要,来源于公式 弧度=角度*π/180 。
这些正五边形的角坐标值以对象 {x: 值, y: 值} 的形式存入 points 变量中,留待绘制之时使用。

最后绘制,核心要点是,按 keys 下标序列取出各个角的坐标值,依次绘制连线即可。本例,使用填充方法 fill() 给五角星着色,也可以使用 stroke() 方法描边,两者同时使用也可以(注意定义一下 strokeStyle 和 lineWidth)。

马黑黑 发表于 2024-5-2 11:57

<h2>附:传统绘制代码</h2>
<div class='mum'>
<cl-cd data-idx="1">&lt;<span class="tDarkRed">canvas</span> <span class="tRed">id</span>=<span class="tMagenta">"canv"</span> width=<span class="tMagenta">"400"</span> height=<span class="tMagenta">"400"</span>&gt;&lt;<span class="tDarkRed">/canvas</span>&gt;</cl-cd>
<cl-cd data-idx="2">&lt;<span class="tDarkRed">script</span>&gt;</cl-cd>
<cl-cd data-idx="3">&nbsp; &nbsp; <span class="tBlue">const</span> ctx = canv.getContext(<span class="tMagenta">'2d'</span>);</cl-cd>
<cl-cd data-idx="4">&nbsp; &nbsp; ctx.beginPath();</cl-cd>
<cl-cd data-idx="5">&nbsp; &nbsp; <span class="tBlue">for</span> (<span class="tBlue">let</span> i = 0; i &lt; 5; i++) {</cl-cd>
<cl-cd data-idx="6">&nbsp; &nbsp; &nbsp; &nbsp; ctx.lineTo(200 + <span class="tRed">Math</span>.cos((18+72*i)/180*<span class="tRed">Math</span>.PI)*190, 200 - <span class="tRed">Math</span>.sin((18+72*i)/180*<span class="tRed">Math</span>.PI)*190);</cl-cd>
<cl-cd data-idx="7">&nbsp; &nbsp; &nbsp; &nbsp; ctx.lineTo(200 + <span class="tRed">Math</span>.cos((54+72*i)/180*<span class="tRed">Math</span>.PI)*76, 200 - <span class="tRed">Math</span>.sin((54+72*i)/180*<span class="tRed">Math</span>.PI)*76);</cl-cd>
<cl-cd data-idx="8">&nbsp; &nbsp; }</cl-cd>
<cl-cd data-idx="9">&nbsp; &nbsp; ctx.closePath();</cl-cd>
<cl-cd data-idx="10">&nbsp; &nbsp; ctx.lineWidth = 3;</cl-cd>
<cl-cd data-idx="11">&nbsp; &nbsp; ctx.strokeStyle = <span class="tMagenta">'red'</span>;</cl-cd>
<cl-cd data-idx="12">&nbsp; &nbsp; ctx.stroke();</cl-cd>
<cl-cd data-idx="13">&lt;<span class="tDarkRed">/script</span>&gt;</cl-cd>
</div>

马黑黑 发表于 2024-5-2 12:04

传统绘制方法中,正五角星的角尖即外圆角假定为外圆(大圆)圆周上的特定点,五角星的内圆角(108度)则为内圆(小圆)圆周上的特定角。

定义内圆角在绘制复杂正五角星时是有必要的,比如不同的路径区域上不同的描边色和填充色,以营造立体感。

再次强调:传统绘制方法中,外圆半径:内圆半径 = 5:2。

红影 发表于 2024-5-2 14:49

这个画法真简洁啊,一笔画成,用0,2,4,1,3的次序画,再用ctx.closePath();连回到0,完美{:4_187:}

红影 发表于 2024-5-2 14:56

马黑黑 发表于 2024-5-2 12:04
传统绘制方法中,正五角星的角尖即外圆角假定为外圆(大圆)圆周上的特定点,五角星的内圆角(108度)则为 ...

传统的还得计算内圆半径,还要考虑内圆角,还是黑黑的一笔连线的五角星更简洁{:4_199:}

小辣椒 发表于 2024-5-2 21:15

电脑预览一个大大的红五角星出来了{:4_173:}

马黑黑 发表于 2024-5-2 21:18

小辣椒 发表于 2024-5-2 21:15
电脑预览一个大大的红五角星出来了

过一下我会发布一个有立体感的五角星

小辣椒 发表于 2024-5-2 21:19

马黑黑 发表于 2024-5-2 21:18
过一下我会发布一个有立体感的五角星

好啊,黑黑是精彩不断,惊喜连连

马黑黑 发表于 2024-5-2 21:21

小辣椒 发表于 2024-5-2 21:19
好啊,黑黑是精彩不断,惊喜连连

这也不是,学习中

小辣椒 发表于 2024-5-2 21:23

马黑黑 发表于 2024-5-2 21:21
这也不是,学习中

反正你不怕胖,我们就期待效果

马黑黑 发表于 2024-5-2 21:24

小辣椒 发表于 2024-5-2 21:23
反正你不怕胖,我们就期待效果

胖胖防台风

马黑黑 发表于 2024-5-2 21:26

红影 发表于 2024-5-2 14:56
传统的还得计算内圆半径,还要考虑内圆角,还是黑黑的一笔连线的五角星更简洁

虽如此,但要着填充色,内外圆的角数据都需要

马黑黑 发表于 2024-5-2 21:27

红影 发表于 2024-5-2 14:49
这个画法真简洁啊,一笔画成,用0,2,4,1,3的次序画,再用ctx.closePath();连回到0,完美

仅仅画正五角星的话,这是好办法

小辣椒 发表于 2024-5-2 21:34

马黑黑 发表于 2024-5-2 21:24
胖胖防台风

最近台风好像我们这里还没有出现

马黑黑 发表于 2024-5-2 21:57

小辣椒 发表于 2024-5-2 21:34
最近台风好像我们这里还没有出现

有备无患,胖胖更安全

红影 发表于 2024-5-3 09:28

马黑黑 发表于 2024-5-2 21:26
虽如此,但要着填充色,内外圆的角数据都需要

是的,看到黑黑的立体五角星了,的确都需要呢。

红影 发表于 2024-5-3 09:29

马黑黑 发表于 2024-5-2 21:27
仅仅画正五角星的话,这是好办法

黑黑的独创{:4_199:}

马黑黑 发表于 2024-5-3 10:20

红影 发表于 2024-5-3 09:29
黑黑的独创

也不是,尝试而已

马黑黑 发表于 2024-5-3 10:21

红影 发表于 2024-5-3 09:28
是的,看到黑黑的立体五角星了,的确都需要呢。

作图理论上细节越丰富图像的效果就会越好
页: [1] 2 3 4
查看完整版本: canvas画布绘制正五角星