本章摘要
本章节将带您从基础图形绘制出发,深入Canvas 2D API的世界。您将学习绘制矩形、折线、多边形、圆及椭圆的方法,理解Canvas渲染上下文的工作机制。通过实践案例和练习,您将掌握关键绘图技巧,激发创造力和想象力。无论您是新手还是资深开发者,本章节都是您图形开发之路上的宝贵资源。
![图形系统开发实战课程 - 基础篇 绘制基本图形](/assets/images/banner_f01.jpg)
第一章 绘制基本图形
本章的内容包括:
- 绘制矩形
- 绘制折线和多边形
- 绘制圆和圆弧
- 绘制椭圆和椭圆弧
1. 矩形
矩形是日常生活和工作中最常见到的图形,Canvas提供了非常简单的方式,只需一行代码就能够绘制矩形,其语法如下:
绘制矩形的边框:
1
| ctx.strokeRect(x, y, width, height)
|
绘制填充的矩形:
1
| ctx.fillRect(x, y, width, height)
|
在这两个方法中只需指定矩形的起始坐标(x,y)和矩形的宽和高,就能够绘制矩形。先看一个示例的运行效果图:
![运行效果](./image_a1/1-1.png)
源代码如下:
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!DOCTYPE html> <html>
<head> <title>绘制基本图形</title> <meta http-equiv="content-type" content="text/html; charset=utf-8"> </head>
<body style="overflow: hidden; margin:0px;"> <canvas id="canvas" width="1200" height="800"></canvas> </body> <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d');
ctx.strokeRect(50, 50, 200, 100);
ctx.fillRect(350, 50, 200, 100); </script>
</html>
|
尝试一下 »
这个示例中:
let canvas = document.getElementById('canvas')
可取得页面中的canvas对象;
let ctx = canvas.getContext('2d');
可取得Canvas对象的渲染上下文对象
ctx.strokeRect(50, 50, 200, 100)
和ctx.fillRect(350, 50, 200, 100)
分别绘制了一个非填充的矩形和一个填充的矩形,其各参数分的含义如下:
- 第一个参数表示矩形的起点横坐标为(x),左侧矩形的x=50, 右侧矩形的x=350
- 第二个参数表示矩形的起点纵坐标为(y),两个矩形的y均为50
- 第三个参数表示矩形的宽度(width),两个矩形的width均为200
- 第四个参数表示矩形的高度(height),两个矩形的height均为100
坐标的原点在左上角,水平方向为x轴,垂直方向为y轴,各个坐标在图中的位置如下图所示:
![运行效果](./image_a1/1-2.png)
Canvas还提供了其他的一些api,可以调整矩形效果,例如使用不用颜色绘制矩形,指定矩形的边框大小等等,均是通过Canvas的渲染上下文对象进行赋值,这些api如下所示:
1 2 3 4 5 6
| ctx.lineWidth = value;
ctx.strokeStyle = color;
ctx.fillStyle = color;
|
其运行效果如下图所示:
![运行效果](./image_a1/1-3.png)
源代码如下:
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!-- 略过html部分的内容--> <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d');
ctx.lineWidth = 10; ctx.strokeRect(100, 300, 200, 100);
ctx.lineWidth = 5; ctx.fillStyle = "#0000FF" ctx.fillRect(400, 300, 200, 100); ctx.strokeStyle = "#FF0000" ctx.strokeRect(400, 300, 200, 100); </script>
|
尝试一下 »
此外,Canvas的渲染上下文还提供了在路径中绘制矩形rect()
和清空一个矩形区域clearRect()
,其参数与strokeRect()
及fillRect()
完全一样,后续文章将会讲解路径的使用方法。清空一个矩形区域,将会实现让清除部分完全透明,通常用于清除Canvas原有内容,以下代码可清除整个Canvas中的内容:
1
| ctx.clearRect(0, 0, canvas.width, canvas.height);
|
2. 折线和多边形
线段的应用非常广泛,不仅包含了普通的直线,还包括了折线,虚线等等,先看一个例子:
![运行效果](./image_a1/2-1.png)
这个例子中我们使用Canvas绘制了四条直线,其中第一条最简单的直线,第二条直线增加了线宽属性,第三条直线增加了颜色属性,第四条直线为虚线,代码如下:
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d');
ctx.beginPath(); ctx.moveTo(50, 50); ctx.lineTo(450, 50); ctx.stroke(); ctx.beginPath(); ctx.moveTo(50, 110); ctx.lineTo(450, 110); ctx.lineWidth=5; ctx.stroke(); ctx.beginPath(); ctx.moveTo(50, 170); ctx.lineTo(450, 170); ctx.strokeStyle="blue"; ctx.stroke(); ctx.beginPath(); ctx.moveTo(50, 230); ctx.lineTo(450, 230); ctx.setLineDash([20, 15]); ctx.stroke(); </script>
|
尝试一下 »
这个示例使用了Canvas的渲染上下文的以下几个方法:
1 2 3 4
| ctx.beginPath() ctx.moveTo(x,y) ctx.lineTo(x,y) ctx.stroke()
|
绘制折线其实是由绘制“路径”这个功能实现的,其过程为:
- 开始绘制路径:ctx.beginPath()
- 将路径移动到起始点:ctx.moveTo(x, y)
- 使用直线连接路径至某个点:ctx.lineTo(x, y),如果需要可以继续使用直线连接路径至某个点
- 设置样式:
指定线宽:ctx.lineWidth=5
指定边框颜色:ctx.strokeColor=“red”
指定填充颜色:ctx.fillColor=“#CCCCCC”
使用虚线模式:ctx.setLineDash([10,10])
- 描边或填充
描边:ctx.stroke()
填充:ctx.fill();
下图标识了上述这四条直线的坐标值,可以更好的理解绘制直线的过程:
![运行效果](./image_a1/2-2.png)
整个绘制的过程如下:
- 1、将画笔移动到坐标(50,50),绘制直线至坐标(450,50);
- 2、将画笔移动到坐标(50,110),绘制直线至坐标(450,110);
- 3、将画笔移动到坐标(50,170),绘制直线至坐标(450,170);
- 4、将画笔移动到坐标(50,230),绘制直线至坐标(450,230);
在绘制过程中,可根据需要改变线宽或颜色等属性。
上述的几张效果图中均包含了灰色的网格线,这个网格线也是通过循环绘制直线功能来实现的,其代码如下:
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
function drawGrid(color, stepX, stepY) { ctx.save(); ctx.beginPath();
for (var i = stepX; i < canvas.width; i += stepX) { ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height) }
for (var i = stepY; i < canvas.height; i += stepY) { ctx.moveTo(0, i); ctx.lineTo(canvas.width, i); }
ctx.lineWidth = 0.5; ctx.strokeStyle = color; ctx.stroke(); ctx.restore(); }
|
我们在来看一个绘制折线图的例子:
![运行效果](./image_a1/2-3.png)
实现的逻辑也很简单,其代码如下:
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <script>
let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); ctx.lineWidth = 6; drawLine([[76, 45], [76, 285], [316, 285]]); ctx.stroke(); drawLine([[90, 51], [76, 23], [62, 51], [90, 51]]); ctx.fill(); drawLine([[310, 271], [338, 285], [310, 299], [310, 271]]); ctx.fill(); drawLine([[105, 256], [192, 169], [222, 227], [309, 140]]); ctx.strokeStyle = "blue"; ctx.stroke(); drawLine([[105, 215], [192, 110], [222, 157], [309, 52]]); ctx.strokeStyle = "red"; ctx.stroke();
function drawLine(pixel) { ctx.beginPath(); ctx.moveTo(pixel[0][0], pixel[0][1]); for (let m = 1; m < pixel.length; m += 1) { ctx.lineTo(pixel[m][0], pixel[m][1]); } } </script>
|
尝试一下 »
接下来我们讲解一下如何绘制虚线,绘制虚线是在设置样式时由ctx.setLineDash([])
方法指定的,这个方法接受一个整数数组作为参数,其含义为:“虚线长度 空格长度 虚线长度 空格长度”,画布在绘制虚线时就会按照这个数组的长度信息重复绘制,使用不同的参数可得到不同的选线效果,我们看看以下示例:
![运行效果](./image_a1/2-4.png)
完整的代码如下:
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); ctx.lineWidth=2; let y = 100; drawDashedLine([2, 2]); drawDashedLine([10, 10]); drawDashedLine([20, 5]); drawDashedLine([15, 4, 4, 4]); drawDashedLine([20, 4, 4, 4, 4, 4, 4, 4]); drawDashedLine([12, 4, 4]); function drawDashedLine(pattern) { ctx.beginPath(); ctx.setLineDash(pattern); ctx.moveTo(500, y); ctx.lineTo(800, y); ctx.stroke(); y += 40; } </script>
|
尝试一下 »
多边形的绘制和绘制折线的过程非常相似,其区别是最后一个点需和第一个点的坐标重合,也可使用closePath()
实现闭合折线。接下来我们看一个绘制多边形(五角星)的例子:
![运行效果](./image_a1/2-5.png)
其源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d');
ctx.beginPath(); ctx.moveTo(100, 20); ctx.lineTo(53, 164); ctx.lineTo(176, 76); ctx.lineTo(24, 76); ctx.lineTo(147, 164) ctx.closePath();
ctx.fillStyle = "red"; ctx.fill(); </script>
|
尝试一下 »
在这个例子中,可以看到在moveTo()
之后,接着lineTo()
,lineTo()
……,就实现了多边形的绘制。
如果没有执行moveTo()
,则表示从当前位置开始绘制,初始位置是坐标原点(0,0)。
这个例子中还执行了一个closePath()
方法,表示从最后一个点至第一个点之间绘制一条直线,闭合了这段折线,首尾进行了相连,实现了多边形的绘制。
在建立路径后,需执行stroke()
或fill()
命令,表示将路径绘制出来。
stroke()
将会对路径进行描边,
fill()
将会对路径进行填充。
3. 圆和圆弧
Canvas 2D API 提供了arc()命令,用于绘制圆或圆弧,其语法如下:
1
| ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
|
参数名 |
说明 |
x |
圆弧中心(圆心)的 x 轴坐标 |
y |
圆弧中心(圆心)的 y 轴坐标 |
radius |
圆弧的半径 |
startAngle |
圆弧的起始点,x 轴方向开始计算,单位以弧度表示 |
endAngle |
圆弧的终点,单位以弧度表示。 |
anticlockwise |
可选的Boolean值(默认值为: false),表示顺时针绘制圆弧,为 true表示逆时针绘制圆弧 |
圆的绘制也是通过建立路径开始的,我们通过几个例子,来了解各个参数的含义,首先看看绘制圆的示例,其运行效果如下图所示:
![运行效果](./image_a1/3-1.png)
这个例子中绘制了一个带边框的圆和一个填充圆,其代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); drawGrid('lightgray', 10, 10);
ctx.beginPath(); ctx.arc(150, 150, 100, 0, 2 * Math.PI); ctx.lineWidth = 4; ctx.stroke();
ctx.beginPath(); ctx.arc(400, 150, 100, 0, 2 * Math.PI); ctx.fillStyle = "blue"; ctx.fill(); </script>
|
尝试一下 »
我们挑选出关键的几条语句:
1 2 3 4 5 6 7
| ctx.arc(150, 150, 100, 0, 2 * Math.PI); ctx.stroke();
ctx.arc(400, 150, 100, 0, 2 * Math.PI); ctx.fill();
|
我们在回顾一下arc()
的语法:
1
| ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
|
前两个参数为圆心的坐标(x,y),第3个参数为圆的半径,下图显示圆心坐标和半径:
![Alt text](./image_a1/3-2.png)
第4和第5个参数是圆的起始角度和终止角度,上面这个例子中,指定开始角度为0,结束角度为2π,因此绘制的是一个完整的圆,而当开始角度至结束角度不到2π时,绘制的就是一个圆弧。需要特别提醒的是:我们之前在学校学习的时候,用到的角度单位通常是度,例如30°/45°/60°/90°/180°/360°等等,而acr()中需使用弧度作为单位,我们知道一周的弧度数是2π(2πr/r),因此可以通过以下代码将角度转为弧度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
function toDegrees(angleInRadians) { return (angleInRadians * 180) / Math.PI; }
function toRadians(angleInDegrees) { return (angleInDegrees * Math.PI) / 180; }
|
接来下看一下圆弧的例子,运行效果如下图:
![Alt text](./image_a1/3-3.png)
在这里例子中绘制了3段圆弧,绿色的半圆,红色的顺时针135°圆弧,蓝色的逆时针135°圆弧,下图中标注了坐标信息:
![Alt text](./image_a1/3-4.png)
这三段弧线都是从0°开始,可以看出0°位于圆心右侧的x坐标轴上,默认时按顺时针方向绘制圆弧,如果anticlockwise=true则逆时针绘制圆弧。绘制这三段圆弧的源代码如下:
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); drawGrid('lightgray', 10, 10);
ctx.lineWidth = 8; ctx.beginPath(); ctx.arc(150, 150, 100, 0, Math.PI); ctx.strokeStyle = "green"; ctx.stroke(); ctx.beginPath(); ctx.arc(400, 150, 100, 0, 135 * Math.PI / 180, true); ctx.strokeStyle = "blue"; ctx.stroke();
ctx.beginPath(); ctx.arc(400, 150, 100, 0, 135 * Math.PI / 180, false); ctx.strokeStyle = "red"; ctx.stroke(); </script>
|
尝试一下 »
接下来我们来看一个有趣的例子,绘制一张笑脸,其效果和代码分别如下:
![运行效果](./image_a1/3-5.png)
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); drawGrid('lightgray', 10, 10);
drawFace(140, 140, 10); function drawFace(x, y, size) { ctx.save(); ctx.translate(x, y); ctx.beginPath(); ctx.arc(0, 0, 10 * size, 0, Math.PI * 2, true); ctx.fillStyle="yellow"; ctx.fill(); ctx.beginPath(); ctx.arc(0, 0, 7 * size, 0, Math.PI, false); ctx.lineWidth = 4; ctx.strokeStyle = "rgb(0,0,0)"; ctx.stroke(); ctx.beginPath(); ctx.arc(0 - 3 * size, 0 - 2.5 * size, size, 0, Math.PI * 2, true); ctx.moveTo(0 + 4 * size, 0 - 2.5 * size); ctx.arc(0 + 3 * size, 0 - 2.5 * size, size, 0, Math.PI * 2, true); ctx.fillStyle="black"; ctx.fill(); ctx.restore(); } </script>
|
尝试一下 »
4. 椭圆和椭圆弧
绘制椭圆和绘制圆的方法非常相似,也是从建立路径开始的,通过ellipse()命令来绘制椭圆或椭圆弧,其语法如下:
1
| ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
|
参数名 |
说明 |
x |
椭圆圆心的 x 轴坐标 |
y |
椭圆圆心的 y 轴坐标 |
radiusX |
椭圆长轴的半径 |
radiusY |
椭圆短轴的半径 |
rotation |
椭圆的旋转角度,单位以弧度表示 |
startAngle |
将要绘制的起始点角度,从 x 轴测量,以弧度表示 |
endAngle |
椭圆将要绘制的结束点角度,以弧度表示 |
anticlockwise |
可选的Boolean值,如果为 true,逆时针绘制圆弧,反之,顺时针绘制 |
我们通过一个例子,来了解各个参数的含义,其运行效果如下图所示:
![Alt text](./image_a1/3-6.png)
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d');
ctx.beginPath(); ctx.ellipse(100, 100, 75, 50, 0, 0, 2 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.ellipse(300, 100, 50, 70, 0, 0, 2 * Math.PI); ctx.stroke(); ctx.beginPath() ctx.moveTo(0, 100) ctx.lineTo(850, 100); ctx.moveTo(100, 0); ctx.lineTo(100, 200) ctx.moveTo(300, 0); ctx.lineTo(300, 200) ctx.setLineDash([4, 4]); ctx.stroke(); </script>
|
尝试一下 »
从这个例子可以看出,绘制椭圆ctx.ellipse()
由中心点坐标、x方向半径、y方向半径、起始角度、终止角度等几个参数绘制而成,其参数和圆的绘制非常相似。
比较特殊的是绘制椭圆多了一个旋转角度参数:
ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
通过一个示例了解一下这个参数,其运行效果和源代码分别如下:
![运行效果](./image_a1/3-7.png)
{.line-numbers}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <script> let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); drawGrid('lightgray', 10, 10);
ctx.lineWidth = 4; ctx.beginPath(); ctx.ellipse(120, 120, 110, 50, 0, 0, 2 * Math.PI); ctx.strokeStyle = "red"; ctx.stroke();
ctx.beginPath(); ctx.ellipse(120, 120, 110, 50, 60 * Math.PI / 180, 0, 2 * Math.PI); ctx.strokeStyle = "blue"; ctx.stroke();
ctx.beginPath(); ctx.ellipse(120, 120, 110, 50, 120 * Math.PI / 180, 0, 2 * Math.PI); ctx.strokeStyle = "green"; ctx.stroke(); </script>
|
尝试一下 »
在这里示例中,三个椭圆除旋转角度外,其他参数均相同,可以看出椭圆以圆心为中心点进行了旋转。
5 本章小结
本节讲解了如何在Canvas绘制矩形、线、多边形、圆、椭圆等内容,并讲解了绘制边和填充、设置线宽、设置颜色等内容,本节内容使用了Canvas 2D API以下方法:
参数名 |
说明 |
strokeRect() |
绘制一个矩形的边框 |
fillRect() |
绘制一个填充的矩形 |
rect() |
在路径中绘制矩形 |
clearRect() |
清除指定矩形区域,让清除部分完全透明 |
beginPath() |
开始一个新的路径 |
closePath() |
将笔点返回到当前子路径起始点 |
moveTo() |
将一个新的子路径的起始点移动到 (x,y) 坐标 |
lineTo() |
绘制直线(使用直线连接子路径的终点到 x,y 坐标) |
arc() |
绘制圆弧路径 |
ellipse() |
绘制椭圆路径 |
stroke() |
绘制当前或已经存在的路径 |
fill() |
填充当前或已存在的路径 |
同时也使用了Canvas 2D API修改了绘图时的以下样式:
参数名 |
说明 |
strokeStyle |
描述画笔(绘制图形)颜色或者样式的属性 |
fillStyle |
描述填充时颜色和样式的属性 |
lineWidth |
设置线段宽度的属性 |
setLineDash() |
填充线时使用虚线模式 |
练习一下
基本形状
- 练习1:绘制一个绿色的(green),填充的,大小为整个画布的矩形;
- 练习2:绘制一个金色的(gold),填充的,大小为100的圆;
- 练习3:绘制一条蓝色的(blue),长度为200, 粗细为6的带箭头的线;
七巧板
七巧板是由五个三角形、一个平行四边形和一个正方形组成,以下是七巧板中各块板的坐标,使用这些数据可以绘制出下面这个图形,你也动手试一试吧。
1 2 3 4 5 6 7 8 9
| [ { "coords": [[100, 50], [340, 50], [220, 170]], "fillColor": "#caff67" }, { "coords": [[100, 50], [220, 170], [100, 290]], "fillColor": "#67becf" }, { "coords": [[340, 50], [340, 170], [280, 230], [280, 110]], "fillColor": "#ef3d61" }, { "coords": [[280, 110], [280, 230], [220, 170]], "fillColor": "#f9f51a" }, { "coords": [[220, 170], [280, 230], [220, 290], [160, 230]], "fillColor": "#a54c09" }, { "coords": [[160, 230], [220, 290], [100, 290]], "fillColor": "#fa8ccc" }, { "coords": [[340, 170], [340, 290], [220, 290]], "fillColor": "#f6ca29" } ]
|
![运行效果](./image_a1/1-exer.png)
尝试一下 »
本文为“图形开发学院”(www.graphanywhere.com)网站原创文章,遵循CC BY-NC-ND 4.0版权协议,商业转载请联系作者获得授权,非商业转载请附上原文出处链接及本声明。
0评论