图形绘制是 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 界面,为应用的视觉层带来更多可能性。