有很多个这练手的,好的差的都有。这次,我演示下用极客的代码,画出最标准的中国国旗,并详细说明代码是怎么写出来的。
绘制规范:
一、严格按照绘制说明;
二、设置基本单位长度,其他长度全以单位长度的倍数表示;
三、坐标系取制作样式上的坐标,制作样式上有的坐标,照取,没有的,全部通过计算;
先把绘制说明复制一遍:
下面先讲下如何作图。
元素并取得
- <canvas>
和
- canvas
对象:
- context
- <canvas id="canvas-flag" width="800"></canvas>
- var canvas = document.getElementById("canvas-flag");
- var context = canvas.getContext("2d");
的
- <canvas>
可以根据需要设置,也可以只设置
- width
,也可以不设置,在后面的代码中再行设置。但不要同时设置
- height
和
- width
,因为长宽比是固定的。
- height
- var WIDTH = canvas.width;
- var UNIT = WIDTH / 30;
- var HEIGHT = canvas.height = UNIT * 20;
因为开始在
元素上已经定义了
- <canvas>
,所以单位长度等于
- width="800"
,高度需要以单位长度自乘以
- WIDTH / 30
。第三句为连续赋值,把
- 20
的值赋给
- UNIT * 20
后,再赋给
- canvas.height
。
- HEIGHT
如果
元素上没有定义
- <canvas>
的话:
- width
- // 只定义了 height
- var HEIGHT = canvas.height;
- var UNIT = WIDTH / 20;
- var WIDTH = canvas.width = UNIT * 30;
- // width, height 都没有定义
- var WIDTH = canvas.width = 800;
- var UNIT = WIDTH / 30;
- var HEIGHT = canvas.height = UNIT * 20;
这样设置过后,后面的单位长度就都可以以这三个变量为基准了。
在这里,我把两个颜色设置成了常量。这个图比较简单,常量的优势不大,但复杂点的图,尤其是一种颜色被用在了好几个块上面的时候,优势就出来了。反复被引用而又未赋给常量的字符串叫魔术字符串,在编码中应尽量消灭。
- var COLOR_RED = "#de2910";
- var COLOR_YELLOW = "#ffde00";
- context.fillStyle = COLOR_RED;
- context.fillRect(0, 0, WIDTH, HEIGHT);
画五角星相对来说要难一些。把五角星的五个角记为顶点 1、顶点 2、顶点 3、顶点 4、顶点 5,通常用笔是这样画星星的:顶点 1 连到顶点 3,连到顶点 5,连到顶点 2,连到顶点 4,最后再连到顶点 1。实际上大于三的奇数角星都可以这样画。
现在回到制图说明上来,每颗五角星的中心(本质上是圆心),半径都是确定的。也就是说,只需要明确顶点 1(暂时称作起始顶点)的角度,就明确了五角星。
在
画布上,横轴的方向是向右的,纵轴的方向是向下的。
- canvas
对于大五角星,起始顶点是向上的,角度是:
\[ - \pi / 2 \]
对于小五角星,有一个顶点是明确的,就是指向大五角星中心的那个顶点。现在来计算其角度,设置大五角中心的坐标是 \((A_x, A_y)\),小五角星中心的坐标是 \((B_x, B_y)\),那么,起始角的角度就是两点连线的斜率的反正切,再加上 \(\pi\)。连线斜率的反正切是横轴与连线的角度,加 \(\pi\) 是为了旋转 180 度,让其指向大五角星的中心。
\[\arctan \frac{B_y - A_y}{B_x - A_x} + \pi \]
也就是说,每颗星星的中点(本质上是圆心)是确定的,半径是确定的,起始角度也可以计算出来。
总共要画六个星星,我们可以写个函数
,接收以下参数:
- drawStar
对象,中心横坐标,中心纵坐标,半径,几角星,填充颜色,起始角。
- context
由于大于三的奇数角星都可以用这个方法来绘制,所有添加了一个几角星的参数。还可以给起始角添加一个向上 \(- \pi / 2\) 的缺省值。
- function drawStar(context, cx, cy, radius, countSides, fillColor, startAngle) {}
在函数体中,首先设置一个画笔指针,用来连接各个顶点。画笔指针初始化在起始顶点;画线(实际上是路径,是看不见的线,显示需要
或
- fill()
)后,画笔指针移动到相间的顶点,再画线,再移动,重复
- stroke()
次。至于填充,这不是这个方法的事,这个方法不负责。
- countSides
- function drawStar(context, cx, cy, radius, countSides, fillColor, startAngle) {
- var pointer = startAngle == null ? - Math.PI / 2 : startAngle;
- // 设置画笔指针,初始点为起始顶点
- // 用 startAngle == null 的原因是为了防止 startAngle = 0 的情况。
- var angle = 2 * Math.PI / countSides;
- // 取得相邻顶点的角度
- context.beginPath();
- for (var i = 0; i < countSides; i++) { // 需要重复 countSides 次
- context.lineTo(cx + radius * Math.cos(pointer), cy + radius * Math.sin(pointer));
- // 画笔指针所在点的坐标:(圆心加上半径乘以正弦值, 圆心加上半径乘以余弦值)
- // beginPath() 后不调用moveTo() 而直接 lineTo(),相当于 moveTo()
- // 就是说,第一次进循环,画笔指针点在动起始顶点,以后就是把画笔指针所在的点连起来
- pointer += 2 * angle;
- // 画笔指针移动到相间的顶点
- }
- context.fillStyle = fillColor;
- context.fill();
- // 填充
- context.closePath();
- }
好了,这样就行了!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>
- Flag of the people's republic of china
- </title>
- </head>
- <body>
- <canvas id="canvas-flag" width="800">
- </canvas>
- <script>
- (function() {
- // Flag of the people's republic of china, made by KilArmd
- var COLOR_RED = "#de2910";
- var COLOR_YELLOW = "#ffde00";
- var canvas = document.getElementById("canvas-flag");
- var context = canvas.getContext("2d");
- var WIDTH = canvas.width;
- var UNIT = WIDTH / 30;
- var HEIGHT = canvas.height = UNIT * 20;
- context.fillStyle = COLOR_RED;
- context.fillRect(0, 0, WIDTH, HEIGHT);
- drawStar(context, 5 * UNIT, 5 * UNIT, 3 * UNIT, 5, COLOR_YELLOW); [[10, 2], [12, 4], [12, 7], [10, 9]].forEach(function(coors) {
- drawStar(context, coors[0] * UNIT, coors[1] * UNIT, UNIT, 5, COLOR_YELLOW, Math.atan((coors[1] - 5) / (coors[0] - 5)) + Math.PI);
- });
- function drawStar(context, cx, cy, radius, countSides, fillColor, startAngle) {
- var pointer = startAngle == null ? -Math.PI / 2 : startAngle;
- var angle = 2 * Math.PI / countSides;
- context.beginPath();
- for (var i = 0; i < countSides; i++) {
- context.lineTo(cx + radius * Math.cos(pointer), cy + radius * Math.sin(pointer));
- pointer += 2 * angle;
- }
- context.fillStyle = fillColor;
- context.fill();
- context.closePath();
- }
- } ());
- </script>
- </body>
- </html>
来源: http://www.cnblogs.com/kilarmd/p/7348568.html