1# CanvasRenderingContext2D对象
2
3
4使用CanvasRenderingContext2D在Canvas画布组件上进行绘制,绘制对象可以是图形、文本、线段、图片等。具体请参考[CanvasRenderingContext2D对象](../reference/apis-arkui/arkui-js/js-components-canvas-canvasrenderingcontext2d.md)。
5
6
7## 画线段
8
9使用moveTo和lineTo画出一条线段,当使用closePath方法时会结束当前路径形成一个封闭图形 。设置quadraticCurveTo(二次贝赛尔曲线)或bezierCurveTo(三次贝赛尔曲线)的值组成图形。
10
11
12```html
13<!-- xxx.hml -->
14<div class="container">
15  <canvas ref="canvas1"></canvas>
16  <select @change="change">
17    <option value="value1"> line </option>
18    <option value="value2"> quadratic </option>
19    <option value="value3"> bezier </option>
20    <option value="value4"> arc/ellipse </option>
21    <option value="value5"> lineJoin/miterLimit </option>
22  </select>
23</div>
24```
25
26
27```css
28/* xxx.css */
29.container{
30  width: 100%;
31  height: 100%;
32  flex-direction: column;
33  justify-content: center;
34  align-items: center;
35  background-color: #F1F3F5;
36}
37canvas{
38  width: 600px;
39  height: 500px;
40  background-color: #fdfdfd;
41  border: 5px solid red;
42}
43select{
44  margin-top: 50px;
45  width: 250px;
46  height: 100px;
47  background-color: white;
48}
49```
50
51
52```js
53// xxx.js
54export default {
55  data:{
56    el: null,
57    ctx: null,
58  },
59  onShow(){
60    this.el = this.$refs.canvas1;
61    this.ctx = this.el.getContext("2d",{antialias: true});
62    // 清除画布上的内容
63    this.ctx.clearRect(0, 0, 600, 500);
64    // 创建一个新的绘制路径
65    this.ctx.beginPath();
66    // 线端点以方形结束
67    this.ctx.lineCap = 'butt';
68    // 描边的宽度
69    this.ctx.lineWidth = 15;
70    // 创建一个新的绘制路径
71    this.ctx.beginPath();
72    // 路径从当前点移动到指定点
73    this.ctx.moveTo(200, 100);
74    // 从当前点到指定点进行路径连接
75    this.ctx.lineTo(400, 100);
76    // 边框绘制
77    this.ctx.stroke();
78    this.ctx.beginPath();
79    // 线端点以圆形结束
80    this.ctx.lineCap = 'round';
81    this.ctx.moveTo(200, 200);
82    this.ctx.lineTo(400, 200);
83    this.ctx.stroke();
84    // 线端点以方形结束
85    this.ctx.beginPath();
86    this.ctx.lineCap = 'square';
87    this.ctx.moveTo(200, 300);
88    this.ctx.lineTo(400, 300);
89    this.ctx.stroke();
90  },
91  change(e){
92    if(e.newValue == 'value1'){
93      this.el = this.$refs.canvas1;
94      this.ctx = this.el.getContext("2d",{antialias: true});
95      this.ctx.clearRect(0, 0, 600, 500);
96      // 上
97      this.ctx.beginPath();
98      this.ctx.lineCap = 'butt';
99      this.ctx.moveTo(200, 100);
100      this.ctx.lineTo(400, 100);
101      this.ctx.stroke();
102      // 中
103      this.ctx.beginPath();
104      this.ctx.lineCap = 'round';
105      this.ctx.moveTo(200, 200);
106      this.ctx.lineTo(400, 200);
107      this.ctx.stroke();
108      // 下
109      this.ctx.beginPath();
110      this.ctx.lineCap = 'square';
111      this.ctx.moveTo(200, 300);
112      this.ctx.lineTo(400, 300);
113      this.ctx.stroke();
114    }else if(e.newValue == 'value2'){
115      this.ctx.clearRect(0, 0, 600, 500);
116      // 上
117      this.ctx.beginPath();
118      this.ctx.moveTo(100, 150);
119      // 二次贝赛尔曲线的路径
120      this.ctx.quadraticCurveTo(300, 50, 500, 150);
121      this.ctx.stroke();
122      // 左
123      this.ctx.beginPath();
124      this.ctx.moveTo(200, 150);
125      this.ctx.quadraticCurveTo(250, 250, 250, 400);
126      this.ctx.stroke();
127      // 右
128      this.ctx.beginPath();
129      this.ctx.moveTo(400, 150);
130      this.ctx.quadraticCurveTo(350, 250, 350, 400);
131      this.ctx.stroke();
132    }else if(e.newValue == 'value3'){
133      this.ctx.clearRect(0, 0, 600, 500);
134      // 下
135      this.ctx.beginPath();
136      this.ctx.moveTo(100, 200);
137      // 三次贝赛尔曲线的路径
138      this.ctx.bezierCurveTo(150, 100, 200, 100,250, 200);
139      this.ctx.stroke();
140      // 左
141      this.ctx.beginPath();
142      this.ctx.moveTo(350, 200);
143      this.ctx.bezierCurveTo(400, 100, 450, 100,500, 200);
144      this.ctx.stroke();
145      // 右
146      this.ctx.beginPath();
147      this.ctx.moveTo(200, 350);
148      this.ctx.bezierCurveTo(250, 500, 350, 500, 400, 350);
149      this.ctx.stroke();
150    }else if(e.newValue == 'value4'){
151      this.ctx.clearRect(0, 0, 600, 500);
152      this.ctx.beginPath();
153      this.ctx.moveTo(100, 200);
154      // 弧线
155      this.ctx.arcTo(150, 300, 350, 300, 150);
156      this.ctx.stroke();
157      this.ctx.beginPath();
158      // 椭圆
159      this.ctx.ellipse(400, 250, 50, 100, Math.PI * 0.25, Math.PI * 0.5 , Math.PI , 1);
160      this.ctx.stroke();
161    }else if(e.newValue == 'value5'){
162      this.ctx.clearRect(0, 0, 600, 500);
163      // 左上
164      this.ctx.beginPath();
165      // 在线段相连处绘制一个扇形
166      this.ctx.lineJoin = 'round';
167      this.ctx.moveTo(100, 100);
168      this.ctx.lineTo(200, 200);
169      this.ctx.lineTo(100, 250);
170      this.ctx.stroke();
171      // 左下
172      this.ctx.beginPath();
173      // 在线段相连处使用三角形为底填充
174      this.ctx.lineJoin = 'bevel';
175      this.ctx.moveTo(100, 300);
176      this.ctx.lineTo(200, 400);
177      this.ctx.lineTo(100, 450);
178      this.ctx.stroke();
179      // 右上
180      this.ctx.beginPath();
181      //线条相交处内角和外角的距离
182      this.ctx.lineJoin = 'miter';
183      this.ctx.miterLimit = 3;
184      this.ctx.moveTo(400, 100);
185      this.ctx.lineTo(450, 200);
186      this.ctx.lineTo(400, 250);
187      // 结束当前路径形成一个封闭路径
188      this.ctx.closePath();
189      this.ctx.stroke();
190      // 右下
191      this.ctx.beginPath();
192      this.ctx.lineJoin = 'miter';
193      this.ctx.miterLimit = 10;
194      this.ctx.moveTo(400, 300);
195      this.ctx.lineTo(450, 400);
196      this.ctx.lineTo(400, 450);
197      this.ctx.closePath();
198      this.ctx.stroke();
199    }
200  },
201}
202```
203
204![zh-cn_image_0000001223064173](figures/zh-cn_image_0000001223064173.gif)
205
206
207## 画边框
208
209全局定义画布(el)及画笔(ctx),初始化创建一个边框宽度为5的长方形。对边框的宽度(lineWidth)、颜色(strokeStyle)、虚化程度(setLineDash)进行改变,选用select组件添加change事件,下拉选择时触发change事件后画出改变后的图形。
210
211```html
212<!-- xxx.hml -->
213<div class="container">
214  <canvas ref="canvas1"></canvas>
215  <select @change="change">
216    <option value="value1">strokeRect</option>
217    <option value="value2">arc</option>
218    <option value="value3">lineDashRect</option>
219    <option value="value4">fillRect</option>
220  </select>
221</div>
222```
223
224```css
225/* xxx.css */
226.container{
227  width: 100%;
228  height: 100%;
229  flex-direction: column;
230  justify-content: center;
231  align-items: center;
232  background-color: #F1F3F5;
233}
234canvas{
235  width: 600px;
236  height: 500px;
237  background-color: #fdfdfd;
238  border: 5px solid red;
239}
240select{
241  margin-top: 50px;
242  width: 250px;
243  height: 100px;
244  background-color: white;
245}
246```
247
248```js
249// xxx.js
250export default {
251  data:{
252    el: null,
253    ctx: null,
254  },
255  onShow(){
256    this.el = this.$refs.canvas1;
257    this.ctx = this.el.getContext("2d",{antialias: true});
258    this.ctx.lineWidth = 5;
259    this.ctx.strokeRect(200, 150, 200, 200);
260  },
261  change(e){
262    if(e.newValue == 'value1'){
263      // 清除画布上的内容
264      this.ctx.clearRect(0,0,600,500);
265      // 边框宽度
266      this.ctx.lineWidth = 5;
267      // 边框颜色
268      this.ctx.strokeStyle = '#110000';
269      // 边框的虚化程度
270      this.ctx.setLineDash([0,0]);
271      // 画具有边框的矩形
272      this.ctx.strokeRect(200, 150, 200, 200);
273    }else if (e.newValue == 'value2'){
274      this.ctx.clearRect(0,0,600,500);
275      this.ctx.lineWidth = 30;
276      this.ctx.strokeStyle = '#0000ff';
277      this.ctx.setLineDash([0,0]);
278      // 画圆
279      this.ctx.arc(300, 250, 150,0,6.28);
280      //进行边框绘制
281      this.ctx.stroke();
282    }else if (e.newValue == 'value3'){
283      this.ctx.clearRect(0,0,600,500);
284      this.ctx.lineWidth = 5;
285      this.ctx.setLineDash([5,5]);
286      this.ctx.strokeRect(200, 150, 200, 200);
287    }else if (e.newValue == 'value4'){
288      this.ctx.clearRect(0,0,600,500);
289      // 画一个有填充颜色的矩形
290      this.ctx.fillStyle = '#0000ff';
291      this.ctx.fillRect(200, 150, 200, 200);
292    }
293  },
294}
295```
296
297
298![zh-cn_image_0000001177466006](figures/zh-cn_image_0000001177466006.gif)
299
300
301## 填充渐变色
302
303添加createLinearGradient和createRadialGradient属性创建渐变容器,接着用addColorStop方法添加多个色块组成渐变色,再设置fillStyle为gradient将渐变色填充到矩形中,最后设置阴影的模糊级别(shadowBlur)、阴影颜色(shadowColor)及阴影偏移量(shadowOffset)。
304
305
306```html
307<!-- xxx.hml -->
308<div class="container">
309  <canvas ref="canvas1"></canvas>
310  <select @change="change">
311    <option value="value1">LinearGradient</option>
312    <option value="value2">RadialGradient</option>
313    <option value="value3">shadowBlur</option>
314    <option value="value4">shadowOffset</option>
315  </select>
316</div>
317```
318
319
320```css
321/* xxx.css */
322.container{
323  width: 100%;
324  height: 100%;
325  flex-direction: column;
326  justify-content: center;
327  align-items: center;
328  background-color: #F1F3F5;
329}
330canvas{
331  width: 600px;
332  height: 500px;
333  background-color: #fdfdfd;
334  border: 5px solid red;
335}
336select{
337  margin-top: 50px;
338  width: 250px;
339  height: 100px;
340  background-color: white;
341}
342```
343
344
345```js
346// xxx.js
347export default {
348  data:{
349    el: null,
350    ctx: null,
351  },
352  onShow(){
353    this.el = this.$refs.canvas1;
354    this.ctx = this.el.getContext("2d",{antialias: true});
355    // 创建一个线性渐变色
356    let gradient = this.ctx.createLinearGradient(100,100, 400,300);
357    // 添加渐变颜色
358    gradient.addColorStop(0.0, 'red');
359    gradient.addColorStop(0.7, 'white');
360    gradient.addColorStop(1.0, 'green');
361    // 填充颜色为渐变色
362    this.ctx.fillStyle = gradient;
363    this.ctx.fillRect(100, 100, 400, 300);
364  },
365  change(e){
366    if(e.newValue == 'value1'){
367      // 清除画布上的内容
368      this.ctx.clearRect(0,0,600,500);
369      let gradient = this.ctx.createLinearGradient(100,100, 400,300);
370      gradient.addColorStop(0.0, 'red');
371      gradient.addColorStop(0.7, 'white');
372      gradient.addColorStop(1.0, 'green');
373      this.ctx.fillStyle = gradient;
374      // 设置绘制阴影时的模糊级别
375      this.ctx.shadowBlur = 0;
376      // 绘制阴影时和原有对象的垂直偏移值
377      this.ctx.shadowOffsetY = 0;
378      // 绘制阴影时和原有对象的水平偏移值
379      this.ctx.shadowOffsetX = 0;
380      this.ctx.fillRect(100, 100, 400, 300);
381    }else if(e.newValue == 'value2'){
382      this.ctx.clearRect(0,0,600,500);
383      // 创建一个径向渐变色
384      let gradient = this.ctx.createRadialGradient(300,250,20,300,250,100);
385      gradient.addColorStop(0.0, 'red');
386      gradient.addColorStop(0.7, 'white');
387      gradient.addColorStop(1.0, 'green');
388      this.ctx.shadowBlur = 0;
389      this.ctx.shadowOffsetY = 0;
390      this.ctx.shadowOffsetX = 0;
391      this.ctx.fillStyle = gradient;
392      this.ctx.fillRect(100, 100, 400, 300);
393    }else if(e.newValue == 'value3'){
394      this.ctx.clearRect(0,0,600,500);
395      let gradient = this.ctx.createLinearGradient(100,100, 400,400);
396      gradient.addColorStop(0.0, 'red');
397      gradient.addColorStop(0.5, 'white');
398      gradient.addColorStop(1, '#17ea35');
399      // 设置绘制阴影时的模糊级别
400      this.ctx.shadowBlur = 30;
401      // 绘制阴影时的阴影颜色
402      this.ctx.shadowColor = 'rgb(229, 16, 16)';
403      this.ctx.fillStyle = gradient;
404      this.ctx.fillRect(100, 100, 400, 300);
405    }else if(e.newValue == 'value4'){
406      this.ctx.clearRect(0,0,600,500);
407      this.ctx.clearRect(0,0,600,500);
408      let gradient = this.ctx.createRadialGradient(300,250,20,300,250,200);
409      gradient.addColorStop(0.0, 'red');
410      gradient.addColorStop(0.5, 'white');
411      gradient.addColorStop(1, '#17ea35');
412      // 设置绘制阴影时的模糊级别
413      this.ctx.shadowBlur = 30;
414      this.ctx.shadowOffsetY = 30;
415      // 绘制阴影时的阴影颜色
416      this.ctx.shadowColor = 'rgb(23, 1, 1)';
417      this.ctx.fillStyle = gradient;
418      this.ctx.fillRect(100, 100, 400, 300);
419    }
420  },
421}
422```
423
424![zh-cn_image_0000001222985801](figures/zh-cn_image_0000001222985801.gif)
425
426
427## 填充文字
428
429先创建文本,再用fillText方法把文字写在画布上。通过globalAlpha属性改变基线透明度,使基线不会挡住文字,再设置textAlign和textBaseline属性确定文字基于基线的位置。
430
431
432```html
433<!-- xxx.hml -->
434<div class="container">
435  <canvas ref="canvas1"></canvas>
436  <select @change="change">
437    <option value="value1">text</option>
438    <option value="value2">textBaseline</option>
439    <option value="value3">textAlign</option>
440  </select>
441</div>
442```
443
444
445```css
446/* xxx.css */
447.container{
448  width: 100%;
449  height: 100%;
450  flex-direction: column;
451  justify-content: center;
452  align-items: center;
453  background-color: #F1F3F5;
454}
455canvas{
456  width: 600px;
457  height: 500px;
458  background-color: #fdfdfd;
459  border: 5px solid red;
460}
461select{
462  margin-top: 50px;
463  width: 250px;
464  height: 100px;
465  background-color: white;
466}
467```
468
469
470```js
471// xxx.js
472export default {
473  data:{
474    el: null,
475    ctx: null,
476  },
477  onShow(){
478    this.el = this.$refs.canvas1;
479    this.ctx = this.el.getContext("2d",{antialias: true});
480    // 创建文本
481    let text = "Hello World";
482    // 设置字体
483    this.ctx.font = '30px';
484    this.ctx.fillText("with:"+this.ctx.measureText(text).width, 200, 300);
485    // 填充字体文本
486    this.ctx.fillText(text, 200, 250);
487  },
488  change(e){
489    if(e.newValue == 'value1'){
490      // 清除画布上的内容
491      this.ctx.clearRect(0,0,600,500);
492      // 开始新的路径
493      this.ctx.beginPath();
494      // 初始化textAlign值
495      this.ctx.textAlign = 'left';
496      // 初始化textBaseline
497      this.ctx.textBaseline = 'alphabetic';
498      // 设置字体
499      this.ctx.font = '30px';
500      let text = "Hello World";
501      // 获取字体width
502      this.ctx.fillText("with:"+this.ctx.measureText(text).width, 200, 300);
503      // 填充字体文本
504      this.ctx.fillText(text, 200, 250);
505    }else if(e.newValue == 'value2'){
506      this.ctx.clearRect(0,0,600,500);
507      this.ctx.beginPath();
508      // 设置透明度
509      this.ctx.globalAlpha = 0.1;
510      // 设置线宽度
511      this.ctx.lineWidth = 10;
512      // 设置线段颜色
513      this.ctx.strokeStyle = '#0000ff';
514      // 从当前点移动到指定点
515      this.ctx.moveTo(0, 240);
516      // 当前点到指定点进行路径连接
517      this.ctx.lineTo(600, 240);
518      this.ctx.stroke();
519      this.ctx.font = '35px';
520      this.ctx.globalAlpha = 1;
521      // 初始化textAlign值
522      this.ctx.textAlign = 'left';
523      // 设置textBaseline
524      this.ctx.textBaseline = 'top';
525      this.ctx.fillText('Top', 50, 240);
526      this.ctx.textBaseline = 'bottom';
527      this.ctx.fillText('Bottom', 200, 240);
528      this.ctx.textBaseline = 'middle';
529      this.ctx.fillText('Middle', 400, 240);
530    }else if(e.newValue == 'value3'){
531      // 清除画布上的内容
532      this.ctx.clearRect(0,0,600,500);
533      this.ctx.beginPath();
534      this.ctx.globalAlpha = 0.1;
535      this.ctx.lineWidth = 10;
536      this.ctx.strokeStyle = '#0000ff';
537      this.ctx.moveTo(300, 0);
538      this.ctx.lineTo(300, 500);
539      this.ctx.stroke();
540      this.ctx.font = '35px';
541      this.ctx.globalAlpha = 1;
542      // 初始化 textBaseline
543      this.ctx.textBaseline = 'alphabetic';
544      // 设置textAlign
545      this.ctx.textAlign = 'left';
546      this.ctx.fillText('textAlign=left',300, 100);
547      this.ctx.textAlign = 'center';
548      this.ctx.fillText('textAlign=center',300, 250);
549      this.ctx.textAlign = 'right';
550      this.ctx.fillText('textAlign=right',300, 400);
551    }
552  }
553}
554```
555
556![zh-cn_image_0000001223064401](figures/zh-cn_image_0000001223064401.gif)
557
558> **说明:**
559>
560> ltr布局模式下start和left一致,rtl布局模式下start和right一致·。
561
562
563## 添加图片
564
565创建图片对象后使用drawImage属性画出图片,给图片设置一些动画样式如scale(缩放)、translate(平移)或rotate(旋转)。
566
567
568```html
569<!-- xxx.hml -->
570<div class="container">
571  <div class="content">
572    <canvas ref="canvas0"></canvas>
573    <text onclick="change">change</text>
574    <canvas ref="canvas1"></canvas>
575    <text onclick="rotate">rotate</text>
576    <canvas ref="canvas2"></canvas>
577    <text onclick="scale">scale</text>
578    <canvas ref="canvas3"></canvas>
579    <text onclick="translate" style="width: 300px;">translate</text>
580    <canvas ref="canvas4"></canvas>
581    <text onclick="transform" style="width: 300px;">transform</text>
582    <canvas ref="canvas5"></canvas>
583    <text onclick="setTransform" style="width: 300px;">setTransform</text>
584    <canvas ref="canvas6"></canvas>
585  </div>
586</div>
587```
588
589
590```css
591/* xxx.css */
592.container{
593  width: 100%;
594  flex-direction: column;
595  background-color: #F1F3F5;
596  align-items: center;
597}
598canvas{
599  width: 600px;
600  height: 300px;
601  margin-bottom: 100px;
602  background-color: #fdfdfd;
603  border: 5px solid red;
604}
605.content{
606  width: 80%;
607  margin-top: 50px;
608  margin-bottom: 50px;
609  display: flex;
610  flex-wrap: wrap;
611  justify-content: space-around;
612}
613text{
614  font-size: 35px;
615  width: 200px;
616  height: 80px;
617  color: white;
618  border-radius: 20px;
619  text-align: center;
620  background-color: #6060e7;
621  margin-bottom: 30px;
622}
623```
624
625
626```js
627// xxx.js
628import promptAction from '@ohos.promptAction';
629export default {
630  data:{
631    compositeOperation: 'source-over'
632  },
633  onShow(){
634    let ctx = this.$refs.canvas0.getContext("2d");
635    // 创建图片对象
636    let img = new Image();
637    // 设置图片路径
638    img.src = 'common/images/2.png';
639    // 设置图片宽度
640    img.width= 150;
641    // 设置图片高度
642    img.height=150;
643    // 图片平铺容器
644    var pat = ctx.createPattern(img, 'repeat');ctx.fillStyle = pat;
645    ctx.fillRect(0, 0, 600, 300);
646  },
647  change(){
648    // 创建画布后得到画笔
649    let ctx = this.$refs.canvas1.getContext("2d");
650    ctx.clearRect(0,0,600,1000);
651    if(this.compositeOperation == "source-over"){
652      this.compositeOperation = "destination-over";
653    }else{
654      this.compositeOperation = "source-over";
655    }
656    ctx.globalCompositeOperation = this.compositeOperation;
657    let img = new Image();
658    img.src = 'common/images/2.png';
659    // 图片成功获取触发方法
660    img.onload = function() {
661      ctx.drawImage(img, 150, 20, 200, 200);
662    };
663    let img1 = new Image();
664    img1.src = 'common/images/3.png';
665    img1.onload = function() {
666      // 画上图片
667      ctx.drawImage(img1, 250, 80, 200, 200);
668    };
669    // 图片获取失败触发方法
670    img1.onerror = function() {
671      promptAction.showToast({message:"error",duration:2000})
672    };
673  },
674  rotate(){
675    let ctx = this.$refs.canvas2.getContext("2d");
676    ctx.clearRect(0,0,600,300);
677    // 旋转
678    ctx.rotate(10 * Math.PI / 180);
679    let img = new Image();
680    img.src = 'common/images/2.png';
681    img.onload = function() {
682      ctx.drawImage(img, 300, 0, 100, 100);
683    };
684  },
685  scale(){
686    let ctx = this.$refs.canvas3.getContext("2d");
687    ctx.clearRect(0,0,600,200);
688    // 缩放
689    ctx.scale(1.3,1.2);
690    let img = new Image();
691    img.src = 'common/images/2.png';
692    img.onload = function() {
693      ctx.drawImage(img, 0, 0, 50, 50);
694    };
695  },
696  translate(){
697    let ctx = this.$refs.canvas4.getContext("2d");
698    ctx.clearRect(0,0,600,300);
699    ctx.translate(10,0);
700    let img = new Image();
701    img.src = 'common/images/2.png';
702    img.onload = function() {
703      ctx.drawImage(img, 0, 50, 300, 200);
704    };
705  },
706  transform(){
707    let ctx = this.$refs.canvas5.getContext("2d");
708    ctx.clearRect(0,0,600,300);
709    ctx.transform(1.1, 0.1, 0.1, 1, 10, 0);
710    let img = new Image();
711    img.src = 'common/images/2.png';
712    img.onload = function() {
713      ctx.drawImage(img, 0, 50, 100, 100);
714     };
715  },
716  setTransform(){
717    let ctx = this.$refs.canvas6.getContext("2d");
718    ctx.clearRect(0,0,600,300);
719    ctx.setTransform(1.1, 0.1, 0.1, 1, 10, 0);
720    let img = new Image();
721    img.src = 'common/images/2.png';
722    img.onload = function() {
723      ctx.drawImage(img, 0, 50, 100, 100);
724    };
725  },
726}
727```
728
729![zh-cn_image_0000001218279600](figures/zh-cn_image_0000001218279600.gif)
730
731> **说明:**
732> - setTransform方法使用的参数和transform()方法相同,但setTransform()方法会重置现有的变换矩阵并创建新的变换矩阵。
733>
734> - 变换后的坐标计算方式(x和y为变换前坐标,x'和y'为变换后坐标):
735>   x' = scaleX \* x + skewY \* y + translateX
736>
737>   y' = skewX \* x + scaleY \* y + translateY
738
739
740## 添加方法
741
742save方法可对画笔样式进行存储,restore可对存储的画笔进行恢复。如下面的示例,先设置画笔为红色,在保存画笔后对画布进行清除并改变画笔为蓝色,当我们直接使用画笔时会画出一个蓝色矩形,对存储的画笔进行恢复后就可画出红色矩形。
743
744
745```html
746<!-- xxx.hml -->
747<div class="container">
748  <div class="content">
749    <canvas ref="canvas"></canvas>
750  </div>
751  <div class="content">
752    <text onclick="save">save</text>
753    <text onclick="clear">clear</text>
754    <text onclick="restore">restore</text>
755  </div>
756</div>
757```
758
759
760```css
761/* xxx.css */
762.container{
763  width: 100%;
764  height: 100%;
765  flex-direction: column;
766  background-color: #F1F3F5;
767  align-items: center;
768}
769canvas{
770  margin-top: 300px;
771  width: 600px;
772  height: 500px;
773  background-color: #fdfdfd;
774  border: 5px solid red;
775}
776.content{
777  width: 80%;
778  margin-top: 50px;
779  margin-bottom: 50px;
780  display: flex;
781  flex-wrap: wrap;
782  justify-content: space-around;
783}
784text{
785  width: 150px;
786  height: 80px;
787  color: white;
788  border-radius: 20px;
789  text-align: center;
790  background-color: #6060e7;
791  margin-bottom: 30px;
792}
793```
794
795
796```js
797// xxx.js
798import promptAction from '@ohos.promptAction';
799export default {
800  data:{
801    ctx: '',
802  },
803  onShow(){
804    this.ctx = this.$refs.canvas.getContext("2d");
805    this.ctx.fillStyle = "red"
806    this.ctx.fillRect(200, 150, 200, 200);
807  },
808  save(){
809    // 画笔储存
810    this.ctx.save();
811    promptAction.showToast({message:"save succeed"});
812  },
813  clear(){
814    this.ctx.clearRect(0,0,600,500);
815    // 该变画笔颜色
816    this.ctx.fillStyle = "#2133d2";
817  },
818  restore(){
819    this.ctx.beginPath();
820    // 画笔恢复
821    this.ctx.restore();
822    this.ctx.fillRect(200, 150, 200, 200);
823  },
824}
825```
826
827![zh-cn_image_0000001177624696](figures/zh-cn_image_0000001177624696.gif)
828