ITPub博客

首页 > 应用开发 > IT综合 > 如何利用OpenHarmony ArkUI的Canvas组件实现涂鸦功能?

如何利用OpenHarmony ArkUI的Canvas组件实现涂鸦功能?

IT综合 作者:OpenHarmony开发者社区 时间:2022-09-20 11:31:52 0 删除 编辑

如何利用OpenHarmony ArkUI的Canvas组件实现涂鸦功能?

简介

ArkUI是一套UI开发框架,提供了开发者进行应用UI开发时所需具备的能力。随着OpenAtom OpenHarmony(以下简称“OpenHarmony”)不断更新迭代,ArkUI也提供了很多新的组件,例如Canvas、OffscreenCanvas、XComponent组件等。

新增的功能可以帮助开发者开发出更流畅、更美观的应用。本篇文章将为大家分享如何通过Canvas组件实现涂鸦功能,用户可以选择空白画布或者简笔图进行自由绘画。


效果展示

以下为效果图:

首页显示了涂鸦的图片以及最后一张空白图片,在点击图片进入涂鸦页面后,可以对画笔的颜色、粗细进行设置。如果涂鸦过程中有错误,可以用橡皮擦将画面擦除,也可点击清除按钮,清空涂鸦的内容,重新进行涂鸦操作。

相关代码已经上传至SIG仓库,链接如下:


目录结构


源码分析

一、Canvas组件介绍

本篇样例主要利用ArkUI的Canvas组件实现涂鸦的功能,首先介绍一下Canvas组件。

Canvas组件主要包含了Canvas和CanvasRenderingContext2D,Canvas提供了画布功能,CanvasRenderingContext2D提供了绘画的属性和方法。通过CanvasRenderingContext2D可以修改画笔的样色、粗细等属性,从而画出各式各样的图形。

以下是Canvas和CanvasRenderingContext2D在样例开发中使用的相关接口信息。

CanvasRenderingContext2D

二、分析源码页面布局

第一个模块是首页布局,首页显示所有涂鸦包含的图片,点击图片可以进入页面;第二个模块是涂鸦模块,可以设置画笔的颜色、边条宽度等。

1. 首页布局


Column()
 
{


      Text('选择涂鸦的图片:').margin('10vp').fontSize('30fp').fontColor(Color.Blue).height('5%')
     Grid() {
       ForEach (this.images, (item, index) => {
          GridItem() {
            Image ( this.images[index] )
             .onClick ((event) => {
               router.push (
                  {
                    url: "pages/detailPage",
                   params: {
                      imgSrc : this.images[index] ,
                    },
                  }
                )
              })
             . width ( '100%' )
             . height ( '100%' )
             . objectFit ( ImageFit.Contain )
          }
        })
      }
     .padding ( { left : this.columnSpace , right : this.columnSpace })
     . columnsTemplate ( "1fr 1fr 1fr" )       // Grid宽度均分成3份
     . rowsTemplate ( "1fr 1fr" )     // Grid高度均分成2份
     . rowsGap ( this.rowSpace )                   // 设置行间距
     . columnsGap ( this.columnSpace )             // 设置列间距
     . width ( '100%' )
     . height ( '95%' )
    }
   . backgroundColor ( Color.Pink )

2. 涂鸦页面 - 画布Canvas的布局通过Stack组件进行包裹,并将Canvas画布覆盖在选择的背景图片之上,这些背景图片主要是水果简笔画。


Stack()
 
{



        Image ( this.imgSrc ) . width ( '100%' ) . height ( '100%' ) . objectFit ( ImageFit.Contain )

        Canvas ( this.context )
         . width ( '100%' )
         . height ( '100%' )
//          .backgroundColor('#00ffff00')
         .onReady (() => {
          })
         .onTouch ((event) => {
            if (event.type === TouchType.Down) {
             this.eventType = 'Down' ;
             this.drawing = true ;
             [this.x , this.y] = [event.touches[0].x , event.touches[0].y] ;
             this.context. beginPath ();
             this.context.lineCap = 'round' ;
              if (this.isEraserMode) {
                //橡皮擦模式
               this.context. clearRect ( this.x , this.y , 20 , 20 );
              }

             console. log ( 'gyf Down' );
            }
            if (event.type === TouchType.Up) {
             this.eventType = 'Up' ;
             this.drawing = false ;
             console. log ( 'gyf Up!' );
             this.context. closePath ();
            }
            if (event.type === TouchType.Move) {
             if ( !this.drawing ) return ;
             this.eventType = 'Move' ;
             console. log ( 'gyf Move' );

              if (this.isEraserMode) {
                //橡皮擦模式
               this.context. clearRect ( event.touches[0].x , event.touches[0].y , 20 , 20 );
              } else {
               this.context.lineWidth = this.lineWidth ;
               this.context.strokeStyle = this.color ;

               this.context. moveTo ( this.x , this.y );
               this.x = event.touches[0].x ;
               this.y = event.touches[0].y ;
               this.context. lineTo ( this.x , this.y );
               this.context. stroke ();
              }
            }
          })

      } . width ( '100%' ) . height ( '75%' )

3.涂鸦页面 - 画笔设置区域的布局


Column() {


       Row() {
         Text('粗细:')

         Button('小').onClick(() => {
           //设置画笔的宽度
           this.lineWidth = 5;
           this.context.lineWidth = this.lineWidth;
           this.isEraserMode = false;
           console.log('gyf small button');
         }).margin($r('app.float.wh_value_10'))

         Button('中').onClick(() => {
           //设置画笔的宽度
           this.lineWidth = 15;
           this.context.lineWidth = this.lineWidth;
           this.isEraserMode = false;
           console.log('gyf middle button');
         }).margin($r('app.float.wh_value_10'))

         Button('大').onClick(() => {
           //设置画笔的宽度
           this.lineWidth = 25;
           this.context.lineWidth = this.lineWidth;
           this.isEraserMode = false;
           console.log('gyf big button');
         }).margin($r('app.float.wh_value_10'))

         Button('超大').onClick(() => {
           //设置画笔的宽度
           this.lineWidth = 40;
           this.context.lineWidth = this.lineWidth;
           this.isEraserMode = false;
           console.log('gyf super big button');
         })
       }.padding($r('app.float.wh_value_10')).margin($r('app.float.wh_value_5'))

       //画笔颜色
       Scroll() {
         Row() {
           Text('颜色:')

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //黑色
               this.color = '#000000';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
               console.log('gyf black button');
             })
             .backgroundColor('#000000')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //红色
               this.color = '#FF0000';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
               console.log('gyf red button');
             })
             .backgroundColor('#FF0000')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //绿色
               this.color = '#00FF00';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
               console.log('gyf green button');
             })
             .backgroundColor('#00FF00')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //蓝色
               this.color = '#0000FF';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#0000FF')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //棕色
               this.color = '#A52A2A';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#A52A2A')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //紫色
               this.color = '#800080';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#800080')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //紫红色
               this.color = '#FF00FF';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#FF00FF')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //深蓝色
               this.color = '#00008B';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#00008B')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //深天蓝
               this.color = '#00BFFF';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#00BFFF')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //绿色
               this.color = '#008000';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#008000')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //青绿色
               this.color = '#32CD32';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#32CD32')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //橙色
               this.color = '#FFA500';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#FFA500')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

           Button(' ', { type: ButtonType.Circle })
             .onClick(() => {
               //黄色
               this.color = '#FFFF00';
               this.context.strokeStyle = this.color;
               this.isEraserMode = false;
             })
             .backgroundColor('#FFFF00')
             .width('40vp')
             .width('40vp')
             .margin($r('app.float.wh_value_10'))

         }.padding('10vp')
       }
       .scrollable(ScrollDirection.Horizontal) // 设置滚动条水平方向滚动
       .margin($r('app.float.wh_value_5'))

       Row() {
         Image('/common/images/eraser.png')
           .onClick(() => {
             //橡皮擦模式
             this.isEraserMode = true;
             console.log('gyf eraser button');
           })
           .width('50vp')
           .height('50vp')
           .margin('10vp')

         Button('清理画板').onClick(() => {
           this.context.clearRect(0, 0, 1000, 1000);
         })
       }
       .margin($r('app.float.wh_value_5'))

     }
     .width('100%')
     .height('25%')
     .alignItems(HorizontalAlign.Start)

三、逻辑代码

逻辑代码存在于Canvas的onTouch事件中,通过TouchType的Down、Up、Move来判断开始、移动和结束的动作。一笔完整的绘制包含一次Down和Up,其中有若干次的Move。橡皮擦模式通过clearRect接口实现擦除的功能。


.onTouch((event) => {


           if (event.type === TouchType.Down) {
             this.eventType = 'Down';
             this.drawing = true;
             [this.x, this.y] = [event.touches[0].x, event.touches[0].y];
             this.context.beginPath();
             this.context.lineCap = 'round';
             if (this.isEraserMode) {
               //橡皮擦模式
               this.context.clearRect(this.x, this.y, 20, 20);
             }

             console.log('gyf Down');
           }
           if (event.type === TouchType.Up) {
             this.eventType = 'Up';
             this.drawing = false;
             console.log('gyf Up!');
             this.context.closePath();
           }
           if (event.type === TouchType.Move) {
             if (!this.drawing) return;
             this.eventType = 'Move';
             console.log('gyf Move');

             if (this.isEraserMode) {
               //橡皮擦模式
               this.context.clearRect(event.touches[0].x, event.touches[0].y, 20, 20);
             } else {
               this.context.lineWidth = this.lineWidth;
               this.context.strokeStyle = this.color;

               this.context.moveTo(this.x, this.y);
               this.x = event.touches[0].x;
               this.y = event.touches[0].y;
               this.context.lineTo(this.x, this.y);
               this.context.stroke();
             }
           }
         })


总结

本文介绍了如何使用ArkUI框架提供的Canvas组件实现涂鸦功能。首先,通过Canvas的onTouch事件来跟踪Down、Move和Up的事件,再设置CanvasRenderingContext2D的相关属性并调用相关的方法,最终实现涂鸦的功能。除了文中分享的涂鸦样例,开发者还可以通过拓展其他相关的属性和方法,实现更多好玩的、高性能的样例。

undefined


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70011554/viewspace-2915406/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: 智能电饭煲
请登录后发表评论 登录
全部评论

注册时间:2022-05-25

  • 博文量
    287
  • 访问量
    125605