前言

代码运行环境:全部基于HarmonyOs NEXT

DevEco Studio:Build Version: 5.0.3.900

API:12

modelVersion:5.0.0

本篇文章,主要是使用Canvas绘制一个简单的画板,可以更改颜色,画笔粗细以及删除操作,主要运用到了CanvasRenderingContext2D中的绘制路径功能,我们可以看下基本实现的效果。



鸿蒙开发:简单自定义一个绘制画板_鸿蒙画布

若在一个画板上进行随意的绘制,少不了画布的存在,鸿蒙当中为我们提供了Canvas组件,使用它,我们可以在上面进行绘制各种想要的图形,共有两个构造参数,可以只接收一个context参数,主要用于设置绘制的能力,除了context参数,也可以接收一个ImageAIOptions参数,主要用于需要AI分析选项的时候,一般传递一个参数就可以。


(context?: CanvasRenderingContext2D | DrawingRenderingContext): CanvasAttribute

CanvasRenderingContext2D比DrawingRenderingContext功能设置更加丰富,而且兼容其自身所带的功能,所以,在绘制元素上,还是建议使用CanvasRenderingContext2D。


设置画布

Canvas是一个组件,我们之间可以像其他组件一样进行使用。

Canvas(this.context)
        .width('100%')
        .height('100%')

传递的是一个CanvasRenderingContext2D对象。


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

RenderingContextSettings是用来配置CanvasRenderingContext2D对象的参数,可以设置是否开启抗锯齿。


路径绘制

路径绘制,包括了手指按下的开始路径,移动路径到指定点,以及手指移动时的点到指定点的路径连接,还有最后的路径结束,这样的流程,才能让线条绘制的更加丝滑,更加符合正常的使用。


.onTouch((event: TouchEvent) => {
          switch (event.type) {
            case TouchType.Down://
              let downTouch = event.touches[0]
              this.context.beginPath()
              this.context.moveTo(downTouch.x, downTouch.y)
              break
            case TouchType.Move:
              let touch = event.touches[0]
              this.context.lineTo(touch.x, touch.y)
              this.context.stroke()
              break
            case TouchType.Up:
              this.context.closePath()
              break
          }
        })

设置抗锯齿

通过设置抗锯齿,可以去掉线条的毛边,让线条变得丝滑顺畅。

Canvas(this.context)
        .width('100%')
        .height('100%')
        .onReady(() => {
          this.settings.antialias = true//打开抗锯齿
          this.context.lineCap = "round"//设置指定线端点的样式,圆形
        })

清除操作


this.context.clearRect(0, 0, this.context.width, this.context.height)


完整代码


@Entry
@Component
struct Index {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  private mColors: string[] =
    ["#000000", "#ffffff", "#FF050C", "#FF7F1D", "#FFE613", "#B2FF29", "#31FFCA", "#2253FF", "#DA25FF", "#FFA687",
      "#ACFFD3", "#98C8FF",
      "#B8ACFF", "#FFCFC5", "#FFDF91"]
  @State showListColor: boolean = true
  @State sliderProgress: string = ""

  build() {
    Column() {

      Canvas(this.context)
        .width('100%')
        .height('100%')
        .onReady(() => {
          this.settings.antialias = true
          this.context.lineCap = "round"
        })
        .onTouch((event: TouchEvent) => {
          switch (event.type) {
            case TouchType.Down:
              let downTouch = event.touches[0]
              this.context.beginPath()
              this.context.moveTo(downTouch.x, downTouch.y)
              break
            case TouchType.Move:
              let touch = event.touches[0]
              this.context.lineTo(touch.x, touch.y)
              this.context.stroke()
              break
            case TouchType.Up:
              this.context.closePath()
              break
          }
        })
        .layoutWeight(1)

      //颜色
      List({ space: 10 }) {
        ForEach(this.mColors, (item: string, _: number) => {
          ListItem() {
            Text()
              .width(20)
              .height(20)
              .backgroundColor(item)
              .borderRadius(20)
              .border({ width: 1, color: "#e8e8e8" })
              .onClick(() => {
                this.context.strokeStyle = item
              })

          }
        })
      }
      .width("100%")
      .height(40)
      .listDirection(Axis.Horizontal)
      .scrollBar(BarState.Off)
      .alignListItem(ListItemAlign.Center)
      .border({ width: { top: 1 }, color: "#e8e8e8" })
      .visibility(this.showListColor ? Visibility.Visible : Visibility.Hidden)

      Slider({
        value: 0,
        min: 0,
        max: 50,
        style: SliderStyle.OutSet
      })
        .showTips(true, this.sliderProgress)
        .trackThickness(5)
        .onChange((value: number, _: SliderChangeMode) => {
          this.sliderProgress = value.toString()
          this.context.lineWidth = value
        })

      Row() {

        Image($r("app.media.canvas_del"))
          .width(30)
          .height(30)
          .borderRadius(30)
          .border({ width: 1, color: "#e8e8e8" })
          .padding(5)
          .onClick(() => {
            //橡皮擦
            this.context.strokeStyle = "#ffffff"
          })

        Image($r("app.media.canvas_clear"))
          .width(30)
          .height(30)
          .borderRadius(30)
          .border({ width: 1, color: "#e8e8e8" })
          .margin({ left: 20 })
          .padding(5)
          .onClick(() => {
            //清空
            this.context.clearRect(0, 0, this.context.width, this.context.height)
          })
      }.width("100%")
      .height(50)
      .border({ width: { top: 1 }, color: "#e8e8e8" })
      .justifyContent(FlexAlign.Center)
    }
  }
}

相关总结

画板,最重要的就是绘制,保证线条绘制的连续性,这一点很重要,还有就是beginPath方法一定要调用,否则更改颜色以及绘制就会出现不连续以及颜色设置错误问题。