鸿蒙NEXT场景化开发:使用ArkUI绘图

289 阅读15分钟

图形绘制是 ArkUI 非常重要的能力之一,开发者可以使用 ArkUI 的基础图形和自定义图形,快速实现复杂的样式场景。在本章中,我们将学习如何使用ArkUI提供的基本图形,以及如何使用Path、Canvas 来创建自定义图形。

创建一个名为MyShape的新的 HarmonyOS 项目,并打开工程开发面板。

8.1 使用基础图形

ArkUI 提供的基础图形有Rect(矩形)、Ellipse(椭圆矩形)、Circle(圆形)等,开发者可以直接调用基础的图形组件来实现页面样式,基础图形使用方法代码如下:

@Entry
@Component
struct Index {

  build() {
   Column({space:20}){
     Rect({ width: '90%', height: 80 })
       .fill('#328E6E')
       .radius(16)
     Rect({ width: '90%', height: 80 })
       .fill('#67AE6E')
       .radius([[0, 0], [32, 32], [32, 32], [32, 32]])
     Rect({ width: '90%', height: 80 })
       .fillOpacity(0)
       .strokeWidth(6)
       .stroke('#90C67C')
       .radius(16)
     Ellipse({ width: '90%', height: 80})
       .fill('#E1EEBC')
     Circle({ width: 150, height: 150 })
       .fill('#81E7AF')
     Circle({ width: 100, height: 100 })
       .fillOpacity(0)
       .strokeWidth(6)
       .stroke('#03A791')
       .strokeDashArray([1, 2])
    }
    .height('100%')
    .width('100%')
  }
}

基础图形组件的使用方法大体相同,首先是组件本身,开发者可以在图形组件的参数中通过 width、height 参数来设置图形的尺寸。在修饰器的使用上,开发者可以使用fill修饰器来设置图形组件的填充色,并通过radius修饰器来设置图形组件的圆角度数。

与fill修饰器使用类似,fillOpacity 修饰器用于设置图形组件填充色的透明度,以此来实现透明填充效果。除此之外,为了对图形实现更加个性化的设置,开发者还可以使用strokeWidth、stroke 来设置图形组件的边框尺寸和边框颜色。

在预览器中,开发者可以预览图形组件的呈现样式,如图 8-1 所示。

图 8-1 图形组件效果预览

8.2 绘制直线图形

ArkUI 除了基础图形外,还支持创建自定义图形,开发者可以使用Path组件来实现自定义图形的构建。

创建一个名为MyCustomShape的新的 HarmonyOS 项目,并打开工程开发面板。

8.2.1 创建CustomShape子组件

为了让自定义图形绘制更加简单易懂,本案例将实现一个自定义图形组件,并将Path组件所涉及到的参数进行声明化处理,代码如下:

@Component
struct CustomShape {
  @Prop x1:number
  @Prop y1:number
  @Prop x2:number
  @Prop y2:number
  @Prop x3:number
  @Prop y3:number
  @Prop x4: number
  @Prop y4: number
  @Prop fillColor: Color
  @Prop fillOpacity: number
  @Prop lineWidth: number
  @Prop lineColor: Color

  build() {
    Path()
      .fill(this.fillColor)
      .fillOpacity(this.fillOpacity)
      .commands(`M${this.x1} ${this.y1} L${this.x2} ${this.y2} L${this.x3} ${this.y3} L${this.x4} ${this.y4} Z`)
      .stroke(this.lineColor)
      .strokeWidth(this.lineWidth)
  }
}

Path 组件的核心修饰器是commands,该修饰器用于设置符合SVG路径描述规范的命令字符串,并将其转换为线条绘制的路径。 commands 修饰器的参数中, M 表示MoveTo,即画笔绘制的起点。L 表示LineTo,即画笔绘制的线段。Z 表示ClosePath,即画笔回到起点构成闭环线段。

在本案例中需要将所有涉及到图形绘制的参数都进行声明化处理,包含 4 个点的 X 轴、Y 轴的坐标,以及自定义图形的填充色、填充色透明度、线段宽度,以及线段大小。

8.2.2 实现直线图形绘制

实现CustomShape 子组件,开发者就可以调用CustomShape 子组件,通过传参的方式来绘制一个自定义图形,比如菱形。代码如下:

@Entry
@Component
struct Index {

  build() {
    Stack() {
      CustomShape({
        x1: 200, y1: 0,
        x2: 400, y2: 200,
        x3: 200, y3: 400,
        x4: 0,   y4: 200,
        fillColor: Color.Red,
        fillOpacity:1,
        lineWidth: 4,
        lineColor:Color.Orange
      })
      Text('福')
        .fontSize(48)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
    }
    .height('100%')
    .width('100%')
  }
}

@Component
struct CustomShape {...}

在Index 视图中,使用Stack 容器组件作为父级容器,调用CustomShape 组件和Text 组件来构建一个菱形且带文本的组合图形。

在预览器中,开发者可以预览自定义图形的呈现样式,如图 8-2 所示。

图 8-2 自定义图形预览

8.3 绘制曲线图形

在SVG路径描述规范中,曲线图形绘制主要使用到的是贝塞尔曲线。除直线绘制外, commands 修饰器同样支持了贝塞尔曲线绘制方法。

创建一个名为MyHeartShape的新的 HarmonyOS 项目,并打开工程开发面板。

8.3.1 创建CustomShape子组件

创建一个自定义图形组件CustomShape,使用Path 组件作为该组件的主要内容,并将其相关参数进行声明化处理,代码如下:

@Component
struct CustomShape {
  @Prop path: string
  @Prop fillColor: Color
  @Prop fillOpacity: number
  @Prop lineWidth: number
  @Prop lineColor: Color

  build() {
    Path()
      .fill(this.fillColor)
      .fillOpacity(this.fillOpacity)
      .commands(this.path)
      .stroke(this.lineColor)
      .strokeWidth(this.lineWidth)
  }
}

CustomShape 组件中,声明一个 string 类型的参数path,并将其传递给 Path 组件的commands 修饰器,如此Path 组件不再接收单点的坐标,而是一整段 path 命令字符串,从而实现灵活绘制任意图形。

8.3.2 实现曲线图形绘制

完成后,在 Index 中调用CustomShape 组件,通过传参的方式来绘制一个心形图形。代码如下:

@Entry
@Component
struct Index {
  build() {
    Column() {
      CustomShape({
        path:
        'M150 80 ' +
          'C150 74 140 50 100 50 ' +
          'C40 50 40 125 40 125 ' +
          'C40 160 80 204 150 240 ' +
          'C220 204 260 160 260 125 ' +
          'C260 125 260 50 200 50 ' +
          'C170 50 150 74 150 80 ' +
          'Z',
        fillColor: Color.Red,
        fillOpacity: 1,
        lineColor: Color.Orange,
        lineWidth: 3
      })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  }
}

@Component
struct CustomShape {...}

在CustomShape 组件的path 的参数中,M 表示MoveTo,即画笔绘制的起点。C表示curveto,即画笔绘制的贝塞尔曲线所经过点。Z 表示ClosePath,即画笔回到起点构成闭环线段。

简单解释下心形图形的绘制路径,如下表格所示:

路径段描述起点 → 终点
M移动到顶部中点
C1上中 → 左上(150,80) → (100,50)
C2左上 → 左侧中部(100,50) → (40,125)
C3左中 → 底部尖(40,125) → (150,240)
C4底部尖 → 右中(150,240) → (260,125)
C5右中 → 右上(260,125) → (200,50)
C6右上 → 上中(200,50) → (150,80)
Z闭合图形自动回起点

在预览器中,开发者可以预览心形图形的呈现样式,如图 8-3 所示。

图 8-3 心形图形预览

8.4 使用Canvas绘制图形

Canvas 组件是 ArkUI 提供的一个非常强大的2D图形绘制组件,常用于复杂图形的绘制,开发者可以将其作为一个自定义画布使用,从而实现各种图形渲染、手绘、图表、签名等功能。

创建一个名为MyCanvas的新的 HarmonyOS 项目,并打开工程开发面板。

8.4.1 创建画布

Canvas 组件需要调用CanvasRenderingContext2D对象来实现图形绘制功能,声明一个CanvasRenderingContext2D类型的状态变量作为Canvas 对象来管理 Canvas组件,该对象用于与Canvas组件进行绑定,代码如下:

private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))

声明一个Path2D 类型的路径对象path,用于记录用户绘制的路径。同时声明一个boolean 类型的状态变量,用于标记是否正在绘图,代码如下:

private path: Path2D = new Path2D()
private drawing: boolean = false

在 build()函数中使用Canvas组件,并将其context 参数传入给Canvas 组件来实现图形绘制功能,代码如下:

@Entry
@Component
struct Index {
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
  private path: Path2D = new Path2D()
  private drawing: boolean = false

  build() {
    Canvas(this.context)
      .width('100%')
      .height('100%')
      .onReady(() => {
        this.context.lineWidth = 3
        this.context.strokeStyle = '#000000'
      })
  }
}

onReady 修饰器用于画布初始化时,或画布大小发生变化时的事件回调,开发者可以在其事件中定义画笔的尺寸和画笔的颜色等设置,比如设置画笔尺寸为 3px,颜色为黑色。

8.4.2 实现绘画功能

下一步,通过为Canvas组件绑定触摸事件来实现画布的自定义绘制,代码如下:

@Entry
@Component
struct Index {
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
  private path: Path2D = new Path2D()
  private drawing: boolean = false

  build() {
    Canvas(this.context)
      .width('100%')
      .height('100%')
      .onReady(() => {...})
      .onTouch((event: TouchEvent) => {
        let touch = event.touches[0]
        let x = touch.x
        let y = touch.y

        if (event.type === TouchType.Down) {
          this.path.moveTo(x, y)
          this.drawing = true
        } else if (event.type === TouchType.Move && this.drawing) {
          this.path.lineTo(x, y)
          this.context.clearRect(0, 0, 10000, 10000)
          this.context.stroke(this.path)
        } else if (event.type === TouchType.Up) {
          this.drawing = false
        }
      })
  }
}

onTouch 修饰器用于获取触摸点的相对画布的x轴和y轴坐标,并将坐标传入路径对象Path2D的moveTo和lineTo方法来实现绘制功能。

在触摸事件处理中,当用户触摸画布时,获取第一个触点的坐标 x, y。当用户按下时,把路径“移动”到当前坐标点,开始绘图。当用户拖动画笔时,从上一个点连线到当前点,实现路径的绘制。最后当用户松开手指时,结束绘图状态,不再连线。

在预览器中,开发者可以绘制自定义线段或图形,来体验Canvas组件的强大能力,如图 8-4 所示。

图 8-4 画布绘制效果预览

8.5 实现进度圆环

在运动场景或专注场景中,设计师通常采用圆环进度的方式表示当前距离目标的完成情况,这在穿戴式设备中尤为常见。ArkUI 很好地将进度功能进行了组件化,开发者可以很简单地调用Progress 组件来显示一个进度条或者进度圆环。

创建一个名为MyProgress的新的 HarmonyOS 项目,并打开工程开发面板。

8.5.1 显示进度圆环

首先,声明 3 个 number 类型的状态变量,表示 3 个进度圆环的进度,代码如下:

@Entry
@Component
struct Index {
  @State progressOne: number = 30
  @State progressTwo: number = 60
  @State progressThree: number = 90

  build() {...}
}

使用 Stack 组件作为父级容器,并在Stack 组件的闭包中放置 3 个Progress 组件,代码如下:

@Entry
@Component
struct Index {
  @State progressOne: number = 30
  @State progressTwo: number = 60
  @State progressThree: number = 90

  build() {
    Stack(){
      Progress({ value: this.progressOne, total: 100, type: ProgressType.Ring })
        .size({width:100,height:100})
        .color(Color.Blue)
        .style({ strokeWidth: 24})
      Progress({ value: this.progressTwo, total: 100, type: ProgressType.Ring })
        .size({width:150,height:150})
        .color(Color.Orange)
        .style({ strokeWidth: 24})
      Progress({ value: this.progressThree, total: 100, type: ProgressType.Ring })
        .size({width:200,height:200})
        .color(Color.Red)
        .style({ strokeWidth: 24})
    }
    .height('100%')
    .width('100%')
  }
}

Progress组件允许开发者设置ProgressType 参数来设置进度条组件的样式,比如Linear(线性)、Ring(环形无刻度)、Eclipse(圆形)、ScaleRing(环形有刻度)、Capsule(胶囊)。

value、total 参数允许开发者传入进度条的当前值和目标值,来呈现进度情况。size 修饰器用于设置进度条组件的尺寸,color 修饰器用于设置进度条组件的进度填充色,style 修饰器用于设置进度条的样式。

在预览器中,开发者预览圆环进度条的呈现效果,如图 8-5 所示。

图 8-5 进度圆环效果预览

8.5.2 实现进度控制

为了灵活设置进度圆环的进度,开发者可以使用Slider 组件来快速调节设置值,代码如下:

@Entry
@Component
struct Index {
  @State progressOne: number = 30
  @State progressTwo: number = 60
  @State progressThree: number = 90

  build() {
    Flex({
      direction: FlexDirection.Column,
      justifyContent: FlexAlign.Center
    }) {
      Stack() {...}
      .height('100%')
      .width('100%')

      Slider({ value: this.progressOne, min: 0, max: 100, style: SliderStyle.InSet })
        .padding({left:40,right:40})
        .showTips(true)
        .onChange((value: number) => {
          this.progressOne = value
        })
      Slider({ value: this.progressTwo, min: 0, max: 100, style: SliderStyle.InSet })
        .padding({left:40,right:40})
        .showTips(true)
        .onChange((value: number) => {
          this.progressOne = value
        })
      Slider({ value: this.progressThree, min: 0, max: 100, style: SliderStyle.InSet })
        .padding({left:40,right:40})
        .showTips(true)
        .onChange((value: number) => {
          this.progressOne = value
        })
    }
  }
}

使用Flex容器组件作为该页面的父级容器,并设置direction、justifyContent 参数让容器内的子组件按照垂直方向排列并居中对齐。

在Progress组件所在的Stack 组件的代码之下,使用 3 个Slider 组件来显示可操作的滑动条。与Progress组件使用方法类型,style 参数用于设置滑动条组件的样式,可设置的样式有OutSet(滑块在滑轨上)、InSet(滑块在滑轨内)、NONE(无滑块)。value、min、max 用于设置滑动条的当前值、最小值、最大值。

showTips 修饰器允许开发者设置滑动时是否显示气泡提示,onChange 修饰器则设置Slider拖动或点击时触发事件回调。

在预览器中,开发者可以调整滑动的位置,来预览圆环进度条的变化效果,如图 8-7 所示。

图 8-7 滑动控制进度圆环效果

8.6 设置颜色渐变

颜色渐变是 ArkUI 提供的一种组件颜色渲染能力,它允许开发者在组件的背景上设置单一或多种颜色,并按照不同的过渡效果来实现缤纷的色彩效果。

创建一个名为MyGradient的新的 HarmonyOS 项目,并打开工程开发面板。

8.6.1 实现分段按钮

为了同时呈现多种颜色渐变效果,开发者可以使用SegmentButton 组件来实现一个分段按钮,当点击不同的分段按钮时,视图显示不同的内容。

由于SegmentButton需要使用到@kit.ArkUI 提供的相关接口,因此需要在页面中导入@kit.ArkUI 模块中的SegmentButton、SegmentButtonOptions工具接口,该接口用于实现调用SegmentButton组件的能力。代码如下:

import { SegmentButton, SegmentButtonOptions } from '@kit.ArkUI'

使用 @State 装饰器声明一个SegmentButtonOptions类型的状态变量作为SegmentButton的对象用于存储SegmentButton按钮的选项数据,代码如下:

@State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({
  buttons: [{ text: '线性渐变' }, { text: '角度渐变' }, { text: '径向渐变' }]
})

声明一个number[] 类型的状态变量,用于定义分段选择器的选中项,即当前分段选择器默认选中的是第几个按钮。代码如下:

@State tabSelectedIndexes: number[] = [0]

在 build()函数中调用SegmentButton 组件,并将SegmentButton 组件中的options、selectedIndexes 参数分别绑定声明好的参数tabOptions、tabSelectedIndexes。值得注意的是,selectedIndexes 参数是@Link类型,因此需要使用“$”关键字建立参数之间的双向绑定。代码如下:

import { SegmentButton, SegmentButtonOptions } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({...})
  @State tabSelectedIndexes: number[] = [0]

  build() {
    Column(){
      Blank()
      SegmentButton({
        options: this.tabOptions,
        selectedIndexes: $tabSelectedIndexes
      })
        .width('90%')
    }
    .height('100%')
    .width('100%')
  }
}

在预览器中,开发者预览分段按钮的呈现效果,如图 8-8 所示。

图 8-8 分段按钮效果预览

8.6.2 实现颜色渐变

常见的颜色渐变有 3 种,分别是线性渐变、角度渐变和径向渐变,ArkUI 对这 3 种渐变效果提供linearGradient、sweepGradient、radialGradient 3 种修饰器来帮助开发者构建。

在Column 组件中创建 3 个Row 组件来实现渐变卡片,并使用Stack 组件作为渐变卡片的父级容器。3 个渐变卡片分别 使用linearGradient、sweepGradient、radialGradient 3 种修饰器来修饰,以此来呈现不同的渐变效果,代码如下:

import { SegmentButton, SegmentButtonOptions } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  @State tabOptions: SegmentButtonOptions = SegmentButtonOptions.tab({...})
  @State tabSelectedIndexes: number[] = [0]

  build() {
    Column(){
      Blank()

      Stack() {
        Row()
          .size({ width: 200, height: 200 })
          .borderRadius(16)
          .linearGradient({
            angle: 90,
            colors: [
              [0xfbc2eb, 0.0],
              [0xa6c1ee, 1.0]
            ]
          })
          .visibility(this.tabSelectedIndexes[0] === 0 ? Visibility.Visible : Visibility.Hidden)

        Row()
          .size({ width: 200, height: 200 })
          .borderRadius(16)
          .sweepGradient({
            center: [100, 100],
            start: 0,
            end: 360,
            colors: [
              [0xff9a9e, 0.0],
              [0xfad0c4, 0.3],
              [0xff9a9e, 1.0]
            ]
          })
          .visibility(this.tabSelectedIndexes[0] === 1 ? Visibility.Visible : Visibility.Hidden)

        Row()
          .size({ width: 200, height: 200 })
          .borderRadius(16)
          .radialGradient({
            center: [100, 100],
            radius: 60,
            colors: [
              [0x8ec5fc, 0.0],
              [0xd57eeb,0.3],
              [0xe0c3fc, 1.0]
            ]
          })
          .visibility(this.tabSelectedIndexes[0] === 2 ? Visibility.Visible : Visibility.Hidden)
      }

      Blank()
      SegmentButton({...})
        .width('90%')
    }
    .height('100%')
    .width('100%')
  }
}

linearGradient 修饰器用于创建从一个方向平滑过渡到另一个方向的线性渐变,开发者可以通过设置 angle 参数控制渐变的方向,例如 angle: 90 表示从上到下的垂直渐变,同时通过 colors 数组设置渐变的颜色值和位置。

sweepGradient 修饰器用于实现角度渐变或扇形渐变,即颜色沿着中心点旋转展开效果开发者可以设置center、start 和 end 角度,以及颜色分布。

radialGradient 修饰器的效果是从一个中心点向外辐射变化的颜色渐变,开发者可以设置设置 center、radius 以及 colors,可以实现光晕或聚焦式的视觉效果。

最后,visibility 修饰器用于控制 UI 显隐,通过判断tabSelectedIndexes 当前所选中的值,来实现不同 UI 的切换显示。

在预览器中,开发者预览颜色渐变的呈现效果和分段按钮切换的效果,如图 8-9 所示。

图 8-9 颜色渐变效果预览

8.7 本章小结

本章围绕 ArkUI 中图形绘制与颜色渐变的能力进行了系统讲解。通过对基础图形的使用、直线与曲线的绘制方法、Canvas 画布的使用技巧、进度圆环的实现方式,以及颜色渐变的展示与交互控制,读者可以全面掌握 ArkUI 在图形层面提供的核心能力与表现手段。

在实际开发中,图形绘制与渐变渲染是构建视觉表达、展示数据形态、增强用户体验的重要方式。希望通过本章的学习,读者能够熟练掌握 ArkUI 图形绘制的基础与进阶能力,灵活运用渐变修饰器和图形组件,打造出具有现代感、结构清晰且富有表现力的 UI 界面,为应用的视觉层带来更多可能性。