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