鸿蒙小案例-你画我猜

1.准备组件(组件布局)
2.实现跟随鼠标画笔画出图案功能
3.实现复制上面的画笔的图案功能
4.其他小功能

1.组件的准备

画布的组件官方给的API是Canvas,需要传递一个参数CanvasRenderingContext2D
在这里插入图片描述
直接搜索API 使用官方案例

  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)

   Canvas(this.context)
        .width('100%')
        .height('100%')
        .backgroundColor('#ffff00')
        .onReady(() => {
          this.context.fillRect(0, 30, 100, 100)
        })
    }
    .width('100%')
    .height('100%')

因为我们参数描述到

不支持多个Canvas共用一个CanvasRenderingContext2D对象

所以,结合我们的显示区域,基础代码精简为

@Entry
@Preview
@Component
struct Nihuawocai2 {
  private context01: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
  private context02: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  /**
   * 1.准备组件(画布布局)
   * 2.实现跟随鼠标画笔画出图案
   * 3.实现复制上面的画笔的图案
   * 4.其他小功能
   */
  build() {
    Row() {
      Column() {
        //自己绘画的区域
        Row(){
          Canvas(this.context01)
            .width('100%')
            .height('100%')
            .backgroundColor(Color.White)
            .onReady(() => {
              //this.context01.fillRect(0, 30, 100, 100)
            })
        }.height('40%').border({ width: { bottom:5 },color:Color.Red })
        //复制绘画的区域
        Row(){
          Canvas(this.context02)
            .width('100%')
            .height('100%')
            .backgroundColor(Color.Grey)
            .onReady(() => {
              //this.context02.fillRect(0, 30, 100, 100)
            })
        }.height('40%')
        //功能区
        Row(){
          Button("清屏")
            .onClick(() =>
            {

            })

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

实现效果:
在这里插入图片描述

2.实现跟随鼠标画笔画出图案

画笔呢肯定是需要用到触摸事件,API如下
在这里插入图片描述
所以Canvas增加onTouch事件

 Canvas(this.context01)
     .width('100%')
     .height('100%')
     .backgroundColor(Color.White)
     .onTouch((event: TouchEvent) =>{

    })

当触摸事件是按下时,开始绘画
在这里插入图片描述
在这里插入图片描述
所以onTouch增加代码

.onTouch((event: TouchEvent) =>{
              //按下时触发,开始绘画
              if (event.type === TouchType.Down)
              {
                  AlertDialog.show({message:'按下手指'})
              }
              //抬起时触发  结束绘画
              if (event.type === TouchType.Up)
              {
                AlertDialog.show({message:'抬起手指'})
              }
              //移动时触发  正在绘画
              if (event.type === TouchType.Move)
              {
                AlertDialog.show({message:'移动手指'})
              }
            })

预览器测试一下,发现 移动手指会一直显示,说明这个触发是没问题的

接下来首先理一下绘画的思路

按下时,准备绘画,从按下的坐标点开始

移动时,正在绘画,随着移动的轨迹,不停记录坐标点,链接上一个坐标点到新坐标点

抬起时,结束绘画,记录当前坐标点为结束点

可能会有多次按下,抬起的操作

所以,我们需要增加一个坐标类,再给一个是否起始点的布尔值

//坐标对象
class zbClass{
  x:number = 0
  y:number = 0
  //按下时记录true,移动时不记录,抬起时记录false
  isStart?:boolean = false
}

增加常量:

是否开始绘画,用来区分多次按下的操作

坐标集合,用来记录绘画轨迹坐标点

//是否开始绘画
isDraw:boolean = false
//坐标点 集合
zbList: zbClass[] = [] 

按下时,记录当前坐标,并增加当前坐标到坐标集合中

//按下时触发,开始绘画
if (event.type === TouchType.Down)
{
    this.isDraw = true
    this.context01X  = event.touches[0].x
    this.context01Y  = event.touches[0].y
    this.zbList.push({
        x:this.context01X,
        y:this.context01Y,
        isStart:true
    })
    //开始绘画
	this.context01.beginPath()
}

开始绘画,参考CanvasRenderingContext2D API官方案例,既将画笔从一个点连接到另一个点,然后不停循环
在这里插入图片描述
将绘画写成一个方法,直接去调用

//移动时触发  正在绘画
if (event.type === TouchType.Move)
{
    if (this.isDraw)
    {
        //绘画中
        this.drawIng(event.touches[0].x, event.touches[0].y)
    }
}

drawIng方法

//绘画过程
  drawIng(x: number, y: number)
  {
      //先移动画笔到起始点
    this.context01.moveTo(this.context01X,this.context01Y)
     //设置绘画边框宽度
    this.context01.lineWidth = 5
    //将画笔从上一个坐标 链接到 手指移动到的新坐标
    this.context01.lineTo(x,y)
    //更新常量坐标点为手指移动坐标点,随着手指移动,形成循环
    this.context01X = x
    this.context01Y = y
    //因为有复制操作,所以,需要保存坐标点
    this.zbList.push({
      x:x,
      y:y
    })
    this.context01.stroke()
  }

抬起手指操作

//抬起时触发  结束绘画
if (event.type === TouchType.Up)
{
    //当前按下手指周期,绘画结束
    this.isDraw = false
    //记录当前周期的结束坐标
    this.zbList.push({
        x: event.touches[0].x,
        y: event.touches[0].y,
        isStart:false
    })
    this.context01.closePath()
}

此时通过预览器测试一下,基本功能已经实现,而且抬起再按下也能继续绘画了

3.实现复制上面的画笔的图案

复制动作可以在全部绘画完后,统一复制,也可以在绘画的同时延迟复制

事后统一复制也就是将集合中的点全部连一遍,比较简单,所以我们边画边复制,在抬起一次手指时开始复制
增加常量:

 //复制画,坐标点
  context02X: number = 0
  context02Y: number = 0
  //定时器,用来循环
  timer: number = -1

增加一个复制方法

//复制动作
cpDraw()
{
  this.context02.lineWidth = 5
  this.timer = setInterval(() =>
  {
    if (this.zbList.length === 0)
    {
      clearInterval(this.timer)
      this.timer = -1
      return
    }
    let p = this.zbList.shift()
    if (p.isStart)
    {
      this.context02.closePath()
      this.context02.beginPath()
      this.context02X = p.x
      this.context02Y = p.y
    } else
    {
      //移动画笔
      this.context02.moveTo(this.context02X, this.context02Y)
      //链接点
      this.context02.lineTo(p.x, p.y)
      //更新点
      this.context02X = p.x
      this.context02Y = p.y
      this.context02.stroke()
    }
  }, 100)
}

测试功能,一切OK

4.其他小功能

清理屏幕
将两个画布的坐标点全部都设置为初始点

Button("清屏")
    .onClick(() =>
    {
        this.context01.clearRect(0, 0, 360, 300)
        this.context02.clearRect(0, 0, 360, 300)
        this.zbList = []
    })

完整代码

@Entry
@Preview
@Component
struct Nihuawocai2 {
  private context01: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
  private context02: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

  //第一个画布的坐标信息
  context01X:number = 0
  context01Y:number = 0
  //是否开始绘画
  isDraw:boolean = false
  //坐标点 集合
  zbList: zbClass[] = []

  //下方绘画坐标点
  context02X: number = 0
  context02Y: number = 0
  //定时器,用来循环
  timer: number = -1
  //绘画过程
  drawIng(x: number, y: number)
  {
    //先移动画笔到起始点
    this.context01.moveTo(this.context01X,this.context01Y)
    //将画笔从上一个坐标 链接到 手指移动到的新坐标
    this.context01.lineTo(x,y)
    //更新常量坐标点为手指移动坐标点,随着手指移动,形成循环
    this.context01X = x
    this.context01Y = y
    //因为有复制操作,所以,需要保存坐标点
    this.zbList.push({
      x:x,
      y:y
    })
    this.context01.stroke()
  }

  //复制动作
  cpDraw()
  {
    this.timer = setInterval(() =>
    {
      if (this.zbList.length === 0)
      {
        clearInterval(this.timer)
        this.timer = -1
        return
      }
      let p = this.zbList.shift()
      if (p.isStart)
      {
        this.context02.closePath()
        this.context02.beginPath()
        this.context02X = p.x
        this.context02Y = p.y
      } else
      {
        //移动画笔
        this.context02.moveTo(this.context02X, this.context02Y)
        //链接点
        this.context02.lineTo(p.x, p.y)
        //更新点
        this.context02X = p.x
        this.context02Y = p.y
        this.context02.stroke()
      }
    }, 100)
  }


  /**
   * 1.准备组件(画布布局)
   * 2.实现跟随鼠标画笔画出图案
   * 3.实现复制上面的画笔的图案
   * 4.其他小功能
   */
  build() {
    Row() {
      Column() {
        //自己绘画的区域
        Row(){
          Canvas(this.context01)
            .width('100%')
            .height('100%')
            .backgroundColor(Color.White)
            .onTouch((event: TouchEvent) =>{
              //按下时触发,开始绘画
              if (event.type === TouchType.Down)
              {
                //当前按下手指周期,绘画开始
                this.isDraw = true
                this.context01X  = event.touches[0].x
                this.context01Y  = event.touches[0].y
                this.zbList.push({
                  x:this.context01X,
                  y:this.context01Y,
                  isStart:true
                })
                //开始绘画
                this.context01.beginPath()

              }
              //抬起时触发  结束绘画
              if (event.type === TouchType.Up)
              {
                //当前按下手指周期,绘画结束
                this.isDraw = false
                this.zbList.push({
                  x: event.touches[0].x,
                  y: event.touches[0].y,
                  isStart:false
                })
                this.context01.closePath()
                this.cpDraw()
              }
              //移动时触发  正在绘画
              if (event.type === TouchType.Move)
              {
                if (this.isDraw)
                {
                  //绘画中
                  this.drawIng(event.touches[0].x, event.touches[0].y)
                }
              }
            })
            .onReady(() => {
              //设置绘画边框宽度
              this.context01.lineWidth = 5
            })
        }.height('40%').border({ width: { bottom:5 },color:Color.Red })
        //复制绘画的区域
        Row(){
          Canvas(this.context02)
            .width('100%')
            .height('100%')
            .backgroundColor(Color.Grey)
            .onReady(() => {
              //设置绘画边框宽度
              this.context02.lineWidth = 5
            })
        }.height('40%')
        //功能区
        Row(){
          Button("清屏")
            .onClick(() =>
            {
              this.context01.clearRect(0, 0, 360, 300)
              this.context02.clearRect(0, 0, 360, 300)
              this.zbList = []
            })

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

//坐标对象
class zbClass{
  x:number = 0
  y:number = 0
  //按下时记录true,移动时不记录,抬起时记录false
  isStart?:boolean = false
}

使用模拟器测试功能OK
模拟器效果
在这里插入图片描述
— end

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐