aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/canvas_api/tutorial/drawing_shapes/index.html
blob: d656a1ad230f41c456b8e2caadcb429c4eeaffba (plain)
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
---
title: 使用canvas来绘制图形
slug: Web/API/Canvas_API/Tutorial/Drawing_shapes
tags:
  - Canvas
  - HTML Canvas
  - HTML5
  - 图形
  - 教程
  - 画布
  - 进阶
translation_of: Web/API/Canvas_API/Tutorial/Drawing_shapes
---
<div>{{CanvasSidebar}} {{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_usage", "Web/API/Canvas_API/Tutorial/Applying_styles_and_colors")}}</div>

<div class="summary">
<p>既然我们已经设置了 <a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_usage">canvas 环境</a>,我们可以深入了解如何在 canvas 上绘制。到本文的最后,你将学会如何绘制矩形,三角形,直线,圆弧和曲线,变得熟悉这些基本的形状。绘制物体到Canvas前,需掌握路径,我们看看到底怎么做。</p>
</div>

<h2 id="栅格">栅格</h2>

<p><img alt="" src="https://mdn.mozillademos.org/files/224/Canvas_default_grid.png" style="float: right; height: 220px; width: 220px;"></p>

<p>在我们开始画图之前,我们需要了解一下画布栅格(canvas grid)以及坐标空间。上一页中的HTML模板中有个宽150px, 高150px的canvas元素。如右图所示,canvas元素默认被网格所覆盖。通常来说网格中的一个单元相当于canvas元素中的一像素。栅格的起点为左上角(坐标为(0,0))。所有元素的位置都相对于原点定位。所以图中蓝色方形左上角的坐标为距离左边(X轴)x像素,距离上边(Y轴)y像素(坐标为(x,y))。在课程的最后我们会平移原点到不同的坐标上,旋转网格以及缩放。现在我们还是使用原来的设置。</p>

<div>
<h2 id="绘制矩形">绘制矩形</h2>

<p>不同于 {{Glossary("SVG")}}{{HTMLElement("canvas")}} 只支持两种形式的图形绘制:矩形和路径(由一系列点连成的线段)。所有其他类型的图形都是通过一条或者多条路径组合而成的。不过,我们拥有众多路径生成的方法让复杂图形的绘制成为了可能。</p>

<div id="section_3">
<p>首先,我们回到矩形的绘制中。canvas提供了三种方法绘制矩形:</p>

<dl>
 <dt>{{domxref("CanvasRenderingContext2D.fillRect", "fillRect(x, y, width, height)")}}</dt>
 <dd>绘制一个填充的矩形</dd>
 <dt>{{domxref("CanvasRenderingContext2D.strokeRect", "strokeRect(x, y, width, height)")}}</dt>
 <dd>绘制一个矩形的边框</dd>
 <dt>{{domxref("CanvasRenderingContext2D.clearRect", "clearRect(x, y, width, height)")}}</dt>
 <dd>清除指定矩形区域,让清除部分完全透明。</dd>
</dl>

<p>上面提供的方法之中每一个都包含了相同的参数。x与y指定了在canvas画布上所绘制的矩形的左上角(相对于原点)的坐标。width和height设置矩形的尺寸。</p>

<p>下面的draw() 函数是前一页中取得的,现在就来使用上面的三个函数。</p>

<h3 id="矩形(Rectangular)例子">矩形(Rectangular)例子</h3>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}</pre>

<p>该例子的输出如下图所示。</p>

<p>{{EmbedLiveSample("矩形(Rectangular)例子", 160, 160, "https://mdn.mozillademos.org/files/245/Canvas_rect.png")}}</p>

<p><code>fillRect()</code>函数绘制了一个边长为100px的黑色正方形。<code>clearRect()</code>函数从正方形的中心开始擦除了一个60*60px的正方形,接着<code>strokeRect()</code>在清除区域内生成一个50*50的正方形边框。</p>

<p>接下来我们能够看到clearRect()的两个可选方法,然后我们会知道如何改变渲染图形的填充颜色及描边颜色。</p>

<p>不同于下一节所要介绍的路径函数(path function),以上的三个函数绘制之后会马上显现在canvas上,即时生效。</p>

<h2 id="绘制路径">绘制路径</h2>

<p>图形的基本元素是路径。路径是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的集合。一个路径,甚至一个子路径,都是闭合的。使用路径绘制图形需要一些额外的步骤。</p>

<ol>
 <li>首先,你需要创建路径起始点。</li>
 <li>然后你使用<a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D#Paths">画图命令</a>去画出路径。</li>
 <li>之后你把路径封闭。</li>
 <li>一旦路径生成,你就能通过描边或填充路径区域来渲染图形。</li>
</ol>

<p>以下是所要用到的函数:</p>

<dl>
 <dt><code>beginPath()</code></dt>
 <dd>新建一条路径,生成之后,图形绘制命令被指向到路径上生成路径。</dd>
 <dt><code>closePath()</code></dt>
 <dd>闭合路径之后图形绘制命令又重新指向到上下文中。</dd>
 <dt><code>stroke()</code></dt>
 <dd>通过线条来绘制图形轮廓。</dd>
 <dt><code>fill()</code></dt>
 <dd>通过填充路径的内容区域生成实心的图形。</dd>
</dl>

<p>生成路径的第一步叫做beginPath()。本质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形、等等)构成图形。而每次这个方法调用之后,列表清空重置,然后我们就可以重新绘制新的图形。</p>

<div class="note"><strong>注意:当前路径为空,即调用beginPath()之后,或者canvas刚建的时候,第一条路径构造命令通常被视为是moveTo(),无论实际上是什么。出于这个原因,你几乎总是要在设置路径之后专门指定你的起始位置。</strong></div>

<p>第二步就是调用函数指定绘制路径,本文稍后我们就能看到了。</p>

<p>第三,就是闭合路径closePath(),不是必需的。这个方法会通过绘制一条从当前点到开始点的直线来闭合图形。如果图形是已经闭合了的,即当前点为开始点,该函数什么也不做。</p>

<div class="note"><strong>注意:当你调用fill()函数时,所有没有闭合的形状都会自动闭合,所以你不需要调用closePath()函数。但是调用stroke()时不会自动闭合</strong><strong style="line-height: 1.5;"></strong></div>

<h3 id="绘制一个三角形">绘制一个三角形</h3>

<p>例如,绘制三角形的代码如下:</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="100" height="100"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 75);
    ctx.lineTo(100, 25);
    ctx.fill();
  }
}
</pre>

<p>输出看上去如下:</p>

<p>{{EmbedLiveSample("绘制一个三角形", 110, 110, "https://mdn.mozillademos.org/files/9847/triangle.png")}}</p>

<h3 id="移动笔触">移动笔触</h3>

<p>一个非常有用的函数,而这个函数实际上并不能画出任何东西,也是上面所描述的路径列表的一部分,这个函数就是<code>moveTo()</code>。或者你可以想象一下在纸上作业,一支钢笔或者铅笔的笔尖从一个点到另一个点的移动过程。</p>

<dl>
 <dt><code>moveTo(<em>x</em>, <em>y</em>)</code></dt>
 <dd>将笔触移动到指定的坐标x以及y上。</dd>
</dl>

<p>当canvas初始化或者<code>beginPath()</code>调用后,你通常会使用<code>moveTo()</code>函数设置起点。我们也能够使用<code>moveTo()</code>绘制一些不连续的路径。看一下下面的笑脸例子。我将用到<code>moveTo()</code>方法(红线处)的地方标记了。</p>

<p>你可以尝试一下,使用下边的代码片。只需要将其复制到之前的<code>draw()</code>函数即可。</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // 绘制
    ctx.moveTo(110, 75);
    ctx.arc(75, 75, 35, 0, Math.PI, false);   // 口(顺时针)
    ctx.moveTo(65, 65);
    ctx.arc(60, 65, 5, 0, Math.PI * 2, true);  // 左眼
    ctx.moveTo(95, 65);
    ctx.arc(90, 65, 5, 0, Math.PI * 2, true);  // 右眼
    ctx.stroke();
  }
}
</pre>

<p>结果看起来是这样的:</p>

<p>{{EmbedLiveSample("%E7%A7%BB%E5%8A%A8%E7%AC%94%E8%A7%A6", 160, 160, "https://mdn.mozillademos.org/files/252/Canvas_smiley.png")}}</p>

<p>如果你想看到连续的线,你可以移除调用的moveTo()。</p>

<div class="note">
<p><strong>注意:需要学习更多关于arc()函数的内容,请看下面的</strong><a href="#圆弧">圆弧</a></p>
</div>

<h3 id="线">线</h3>

<p>绘制直线,需要用到的方法<code>lineTo()</code></p>

<dl>
 <dt>{{domxref("CanvasRenderingContext2D.lineTo", "lineTo(x, y)")}}</dt>
 <dd>绘制一条从当前位置到指定x以及y位置的直线。</dd>
</dl>

<p>该方法有两个参数:x以及y ,代表坐标系中直线结束的点。开始点和之前的绘制路径有关,之前路径的结束点就是接下来的开始点,等等。。。开始点也可以通过<code>moveTo()</code>函数改变。</p>

<p>下面的例子绘制两个三角形,一个是填充的,另一个是描边的。</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
  var ctx = canvas.getContext('2d');

  // 填充三角形
  ctx.beginPath();
  ctx.moveTo(25, 25);
  ctx.lineTo(105, 25);
  ctx.lineTo(25, 105);
  ctx.fill();

  // 描边三角形
  ctx.beginPath();
  ctx.moveTo(125, 125);
  ctx.lineTo(125, 45);
  ctx.lineTo(45, 125);
  ctx.closePath();
  ctx.stroke();
  }
}</pre>

<p>这里从调用<code>beginPath()</code>函数准备绘制一个新的形状路径开始。然后使用<code>moveTo()</code>函数移动到目标位置上。然后下面,两条线段绘制后构成三角形的两条边。</p>

<p>{{EmbedLiveSample("%E7%BA%BF", 160, 160, "https://mdn.mozillademos.org/files/238/Canvas_lineTo.png")}}</p>

<p>你会注意到填充与描边三角形步骤有所不同。正如上面所提到的,因为路径使用填充(fill)时,路径自动闭合,使用描边(stroke)则不会闭合路径。如果没有添加闭合路径<code>closePath()</code>到描边三角形函数中,则只绘制了两条线段,并不是一个完整的三角形。</p>

<h3 id="圆弧">圆弧</h3>

<p>绘制圆弧或者圆,我们使用<code>arc()</code>方法。当然可以使用<code>arcTo()</code>,不过这个的实现并不是那么的可靠,所以我们这里不作介绍。</p>

<dl>
 <dt>{{domxref("CanvasRenderingContext2D.arc", "arc(x, y, radius, startAngle, endAngle, anticlockwise)")}}</dt>
 <dd>画一个以(x,y)为圆心的以radius为半径的圆弧(圆),从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成。</dd>
 <dt>{{domxref("CanvasRenderingContext2D.arcTo", "arcTo(x1, y1, x2, y2, radius)")}}</dt>
 <dd>根据给定的控制点和半径画一段圆弧,再以直线连接两个控制点。</dd>
</dl>

<p>这里详细介绍一下arc方法,该方法有六个参数:<code>x,y</code>为绘制圆弧所在圆上的圆心坐标。<font><code>radius</code>为半径。<code>startAngle</code>以及<code>endAngle</code>参数用弧度定义了开始以及结束的弧度。这些都是以x轴为基准。参数</font><code style="font-style: normal; line-height: 1.5;">anticlockwise</code>为一个布尔值。为true时,是逆时针方向,否则顺时针方向。</p>

<div class="note">
<p><strong>注意:<code>arc()</code>函数中表示角的单位是弧度,不是角度。角度与弧度的js表达式:</strong></p>

<p><strong>弧度=(Math.PI/180)*角度。</strong></p>
</div>

<p>下面的例子比上面的要复杂一下,下面绘制了12个不同的角度以及填充的圆弧。</p>

<p>下面两个<code>for</code>循环,生成圆弧的行列(x,y)坐标。每一段圆弧的开始都调用<code>beginPath()</code>。代码中,每个圆弧的参数都是可变的,实际编程中,我们并不需要这样做。</p>

<p>x,y坐标是可变的。半径(radius)和开始角度(startAngle)都是固定的。结束角度(endAngle)在第一列开始时是180度(半圆)然后每列增加90度。最后一列形成一个完整的圆。</p>

<p><code style="font-style: normal; line-height: 1.5;">clockwise</code>语句作用于第一、三行是顺时针的圆弧,<code>anticlockwise</code>作用于二、四行为逆时针圆弧。<code>if</code>语句让一、二行描边圆弧,下面两行填充路径。</p>

<div class="note">
<p><strong>注意:</strong> 这个示例所需的画布大小略大于本页面的其他例子: 150 x 200 像素。</p>
</div>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="150" height="200"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    for(var i = 0; i &lt; 4; i++){
      for(var j = 0; j &lt; 3; j++){
        ctx.beginPath();
        var x = 25 + j * 50; // x 坐标值
        var y = 25 + i * 50; // y 坐标值
        var radius = 20; // 圆弧半径
        var startAngle = 0; // 开始点
        var endAngle = Math.PI + (Math.PI * j) / 2; // 结束点
        var anticlockwise = i % 2 == 0 ? false : true; // 顺时针或逆时针

        ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

        if (i&gt;1){
          ctx.fill();
        } else {
          ctx.stroke();
        }
      }
    }
  }
}
</pre>

<p>{{EmbedLiveSample("%E5%9C%86%E5%BC%A7", 160, 210, "https://mdn.mozillademos.org/files/204/Canvas_arc.png")}}</p>

<h3 id="二次贝塞尔曲线及三次贝塞尔曲线">二次贝塞尔曲线及三次贝塞尔曲线</h3>

<p>下一个十分有用的路径类型就是<a href="https://zh.wikipedia.org/wiki/%E8%B2%9D%E8%8C%B2%E6%9B%B2%E7%B7%9A" rel="external">贝塞尔曲线</a>。二次及三次贝塞尔曲线都十分有用,一般用来绘制复杂有规律的图形。</p>

<dl>
 <dt><code>quadraticCurveTo(cp1x, cp1y, x, y)</code></dt>
 <dd>绘制二次贝塞尔曲线,<code>cp1x,cp1y</code>为一个控制点,<code>x,y为</code>结束点。</dd>
 <dt><code>bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)</code></dt>
 <dd>绘制三次贝塞尔曲线,<code>cp1x,cp1y</code>为控制点一,<code>cp2x,cp2y</code>为控制点二,<code>x,y</code>为结束点。</dd>
</dl>

<p><img alt="" src="https://mdn.mozillademos.org/files/223/Canvas_curves.png" style="float: right; height: 190px; width: 190px;">右边的图能够很好的描述两者的关系,二次贝塞尔曲线有一个开始点(蓝色)、一个结束点(蓝色)以及一个控制点(红色),而三次贝塞尔曲线有两个控制点。</p>

<p>参数x、y在这两个方法中都是结束点坐标。<code>cp1x,cp1y</code>为坐标中的第一个控制点,<code>cp2x,cp2y</code>为坐标中的第二个控制点。</p>

<p>使用二次以及三次贝塞尔曲线是有一定的难度的,因为不同于像Adobe Illustrators这样的矢量软件,我们所绘制的曲线没有给我们提供直接的视觉反馈。这让绘制复杂的图形变得十分困难。在下面的例子中,我们会绘制一些简单有规律的图形,如果你有时间以及更多的耐心,很多复杂的图形你也可以绘制出来。</p>

<p>下面的这些例子没有多少困难。这两个例子中我们会连续绘制贝塞尔曲线,最后形成复杂的图形。</p>

<h4 id="二次贝塞尔曲线">二次贝塞尔曲线</h4>

<p>这个例子使用多个贝塞尔曲线来渲染对话气泡。</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    // 二次贝塞尔曲线
    ctx.beginPath();
    ctx.moveTo(75, 25);
    ctx.quadraticCurveTo(25, 25, 25, 62.5);
    ctx.quadraticCurveTo(25, 100, 50, 100);
    ctx.quadraticCurveTo(50, 120, 30, 125);
    ctx.quadraticCurveTo(60, 120, 65, 100);
    ctx.quadraticCurveTo(125, 100, 125, 62.5);
    ctx.quadraticCurveTo(125, 25, 75, 25);
    ctx.stroke();
   }
}
</pre>

<p>{{EmbedLiveSample("%E4%BA%8C%E6%AC%A1%E8%B4%9D%E5%A1%9E%E5%B0%94%E6%9B%B2%E7%BA%BF", 160, 160, "https://mdn.mozillademos.org/files/243/Canvas_quadratic.png")}}</p>

<h4 id="三次贝塞尔曲线">三次贝塞尔曲线</h4>

<p>这个例子使用贝塞尔曲线绘制心形。</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

     //三次贝塞尔曲线
    ctx.beginPath();
    ctx.moveTo(75, 40);
    ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
    ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
    ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
    ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
    ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
    ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
    ctx.fill();
  }
}
</pre>

<p>{{EmbedLiveSample("%E4%B8%89%E6%AC%A1%E8%B4%9D%E5%A1%9E%E5%B0%94%E6%9B%B2%E7%BA%BF", 160, 160, "https://mdn.mozillademos.org/files/207/Canvas_bezier.png")}}</p>

<h3 id="矩形">矩形</h3>

<p>直接在画布上绘制矩形的三个额外方法,正如我们开始所见的<a href="#绘制矩形">绘制矩形</a>,同样,也有rect()方法,将一个矩形路径增加到当前路径上。</p>

<dl>
 <dt><code>rect(<em>x</em>, <em>y</em>, <em>width</em>, <em>height</em>)</code></dt>
 <dd>绘制一个左上角坐标为(x,y),宽高为width以及height的矩形。</dd>
</dl>

<p>当该方法执行的时候,moveTo()方法自动设置坐标参数(0,0)。也就是说,当前笔触自动重置回默认坐标。</p>

<h3 id="组合使用">组合使用</h3>

<p>目前为止,每一个例子中的每个图形都只用到一种类型的路径。然而,绘制一个图形并没有限制使用数量以及类型。所以在最后的一个例子里,让我们组合使用所有的路径函数来重现一款著名的游戏。</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="150" height="150"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;</pre>
</div>

<pre class="brush: js notranslate">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    roundedRect(ctx, 12, 12, 150, 150, 15);
    roundedRect(ctx, 19, 19, 150, 150, 9);
    roundedRect(ctx, 53, 53, 49, 33, 10);
    roundedRect(ctx, 53, 119, 49, 16, 6);
    roundedRect(ctx, 135, 53, 49, 33, 10);
    roundedRect(ctx, 135, 119, 25, 49, 10);

    ctx.beginPath();
    ctx.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7, false);
    ctx.lineTo(31, 37);
    ctx.fill();

    for(var i = 0; i &lt; 8; i++){
      ctx.fillRect(51 + i * 16, 35, 4, 4);
    }

    for(i = 0; i &lt; 6; i++){
      ctx.fillRect(115, 51 + i * 16, 4, 4);
    }

    for(i = 0; i &lt; 8; i++){
      ctx.fillRect(51 + i * 16, 99, 4, 4);
    }

    ctx.beginPath();
    ctx.moveTo(83, 116);
    ctx.lineTo(83, 102);
    ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
    ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
    ctx.lineTo(111, 116);
    ctx.lineTo(106.333, 111.333);
    ctx.lineTo(101.666, 116);
    ctx.lineTo(97, 111.333);
    ctx.lineTo(92.333, 116);
    ctx.lineTo(87.666, 111.333);
    ctx.lineTo(83, 116);
    ctx.fill();

    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.moveTo(91, 96);
    ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
    ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
    ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
    ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
    ctx.moveTo(103, 96);
    ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
    ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
    ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
    ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
    ctx.fill();

    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
    ctx.fill();

    ctx.beginPath();
    ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
    ctx.fill();
  }
}

// 封装的一个用于绘制圆角矩形的函数.

function roundedRect(ctx, x, y, width, height, radius){
  ctx.beginPath();
  ctx.moveTo(x, y + radius);
  ctx.lineTo(x, y + height - radius);
  ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
  ctx.lineTo(x + width - radius, y + height);
  ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
  ctx.lineTo(x + width, y + radius);
  ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
  ctx.lineTo(x + radius, y);
  ctx.quadraticCurveTo(x, y, x, y + radius);
  ctx.stroke();
}
</pre>

<p>结果画面如下:</p>

<p>{{EmbedLiveSample("%E7%BB%84%E5%90%88%E4%BD%BF%E7%94%A8", 160, 160, "https://mdn.mozillademos.org/files/9849/combinations.png")}}</p>

<p>我们不会很详细地讲解上面的代码,因为事实上这很容易理解。重点是绘制上下文中使用到了fillStyle属性,以及封装函数(例子中的<code>roundedRect()</code>)。使用封装函数对于减少代码量以及复杂度十分有用。</p>

<p>在稍后的课程里,我们会讨论<code>fillStyle</code>样式的更多细节。这章节中,我们对<code>fillStyle</code>样式所做的仅是改变填充颜色,由默认的黑色到白色,然后又是黑色。</p>

<h2 id="Path2D_对象">Path2D 对象</h2>

<p>正如我们在前面例子中看到的,你可以使用一系列的路径和绘画命令来把对象“画”在画布上。为了简化代码和提高性能,{{domxref("Path2D")}}对象已可以在较新版本的浏览器中使用,用来缓存或记录绘画命令,这样你将能快速地回顾路径。</p>

<p>怎样产生一个Path2D对象呢?</p>

<dl>
 <dt>{{domxref("Path2D.Path2D", "Path2D()")}}</dt>
 <dd><code>Path2D()</code>会返回一个新初始化的Path2D对象(可能将某一个路径作为变量——创建一个它的副本,或者将一个包含SVG path数据的字符串作为变量)。</dd>
</dl>

<pre class="brush: js line-numbers  language-js notranslate"><code class="language-js">new Path2D();     // 空的Path对象
new Path2D(path); // 克隆Path对象
new Path2D(d);    // 从SVG建立Path对象</code></pre>

<p>所有的路径方法比如<code>moveTo</code>, <code>rect</code>, <code>arc</code><code>quadraticCurveTo</code>等,如我们前面见过的,都可以在Path2D中使用。</p>

<p>Path2D API 添加了 <code>addPath</code>作为将<code>path</code>结合起来的方法。当你想要从几个元素中来创建对象时,这将会很实用。比如:</p>

<dl>
 <dt><strong>{{domxref("Path2D.addPath", "Path2D.addPath(path [, transform])")}}</strong></dt>
 <dd>添加了一条路径到当前路径(可能添加了一个变换矩阵)。</dd>
</dl>

<h3 id="Path2D_示例">Path2D 示例</h3>

<p>在这个例子中,我们创造了一个矩形和一个圆。它们都被存为Path2D对象,后面再派上用场。随着新的Path2D API产生,几种方法也相应地被更新来使用Path2D对象而不是当前路径。在这里,带路径参数的<code>stroke</code><code>fill</code>可以把对象画在画布上。</p>

<div class="hidden">
<pre class="brush: html notranslate">&lt;html&gt;
 &lt;body onload="draw();"&gt;
   &lt;canvas id="canvas" width="130" height="100"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
</div>

<pre class="brush: js;highlight[6,9] line-numbers  language-js notranslate"><code class="language-js">function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    var rectangle = new Path2D();
    rectangle.rect(10, 10, 50, 50);

    var circle = new Path2D();
    circle.moveTo(125, 35);
    circle.arc(100, 35, 25, 0, 2 * Math.PI);

    ctx.stroke(rectangle);
    ctx.fill(circle);
  }
}</code></pre>

<p>{{EmbedLiveSample("Path2D_%E7%A4%BA%E4%BE%8B", 130, 110, "https://mdn.mozillademos.org/files/9851/path2d.png")}}</p>

<h3 id="使用_SVG_paths">使用 SVG paths</h3>

<p>新的Path2D API有另一个强大的特点,就是使用SVG path data来初始化canvas上的路径。这将使你获取路径时可以以SVG或canvas的方式来重用它们。</p>

<p>这条路径将先移动到点 <code>(M10 10)</code> 然后再水平移动80个单位<code>(h 80)</code>,然后下移80个单位 <code>(v 80)</code>,接着左移80个单位 <code>(h -80)</code>,再回到起点处 (<code>z</code>)。你可以在<a href="https://developer.mozilla.org/en-US/docs/Web/API/Path2D.Path2D#Using_SVG_paths">Path2D constructor</a> 查看这个例子。</p>

<pre class="brush: js; line-numbers  language-js notranslate"><code class="language-js">var p = new Path2D("M10 10 h 80 v 80 h -80 Z");</code></pre>
</div>

<div>{{PreviousNext("Web/API/Canvas_API/Tutorial/Basic_usage", "Web/API/Canvas_API/Tutorial/Applying_styles_and_colors")}}</div>
</div>