# XBorderRadiusButtonBackground **Repository Path**: SourHarbor/XBorderRadiusButtonBackground ## Basic Information - **Project Name**: XBorderRadiusButtonBackground - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-06-11 - **Last Updated**: 2025-06-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 在现代的UI界面中,常会出现非常规的圆角样式需求。本文深入解析一种基于Canvas的动态圆角绘制方案,可完美实现正负半径值组合形成的内外圆角混合效果。 ![](https://alliance-communityfile-drcn.dbankcdn.com/FileServer/getFile/cmtybbs/398/970/576/2850086000398970576.20250611162922.79664206680968662858940189365620:50001231000000:2800:7F59D8532751455C7385343CBA849F0195D7688325AA447A225B3D0875E96A5A.png) ## 核心实现原理 条件判断: 当四个角均为内圆角或均为外圆角时,直接使用ArkUI的通用borderRadius属性: ```arkts if ((this.topRadius >= 0 && this.bottomRadius >= 0) || (this.topRadius < 0 && this.bottomRadius < 0)) { Column() .height('100%') .width('100%') .borderRadius(Math.abs(this.topRadius + this.bottomRadius) / 2) .backgroundColor(this.active ? this.activeColor : this.inactiveColor) .onClick(() => { this.action(); }) } ``` 否则使用Canvas绘制: ```arkts else { Canvas(this.context).height('100%').width('100%') .onReady(() => { this.drawCanvas(); }) } ``` ## 混合圆角绘制算法 根据参数正负组合,进入两种绘制模式: ### 模式一:顶部内圆角 + 底部外圆角 ```arkts if (this.topRadius >= 0 && this.bottomRadius < 0) { let p1: Point = { x: Math.abs(this.bottomRadius) + this.topRadius, y: this.topRadius } let p2: Point = { x: this.context.width - Math.abs(this.bottomRadius) - this.topRadius, y: this.topRadius } let p3: Point = { x: 0, y: this.context.height - Math.abs(this.bottomRadius) }; let p4: Point = { x: this.context.width, y: this.context.height - Math.abs(this.bottomRadius) } this.context.moveTo(0, this.context.height); this.context.arc(p3.x, p3.y, Math.abs(this.bottomRadius), Math.PI / 2, 0, true); this.context.lineTo(p1.x - this.topRadius, p1.y); this.context.arc(p1.x, p1.y, this.topRadius, Math.PI, -Math.PI / 2); this.context.lineTo(p2.x, p2.y - this.topRadius); this.context.arc(p2.x, p2.y, this.topRadius, -Math.PI / 2, 0); this.context.lineTo(p4.x - Math.abs(this.bottomRadius), p4.y); this.context.arc(p4.x, p4.y, Math.abs(this.bottomRadius), Math.PI, Math.PI / 2, true); this.context.stroke(); this.context.fill(); } ``` ### 模式二:顶部外圆角 + 底部内圆角 ```arkts else if (this.topRadius < 0 && this.bottomRadius >= 0) { let p1: Point = { x: 0, y: Math.abs(this.topRadius) } let p2: Point = { x: this.context.width, y: Math.abs(this.topRadius) } let p3: Point = { x: this.bottomRadius + Math.abs(this.topRadius), y: this.context.height - this.bottomRadius }; let p4: Point = { x: this.context.width - this.bottomRadius - Math.abs(this.topRadius), y: this.context.height - this.bottomRadius } this.context.moveTo(0, 0); this.context.arc(p1.x, p1.y, Math.abs(this.topRadius), -Math.PI / 2, 0); this.context.lineTo(Math.abs(this.topRadius), this.context.height - this.bottomRadius); this.context.arc(p3.x, p3.y, this.bottomRadius, Math.PI, Math.PI / 2, true); this.context.lineTo(p4.x, this.context.height); this.context.arc(p4.x, p4.y, this.bottomRadius, Math.PI / 2, 0, true); this.context.lineTo(p2.x - Math.abs(this.topRadius), p2.y); this.context.arc(p2.x, p2.y, Math.abs(this.topRadius), Math.PI, -Math.PI / 2); this.context.lineTo(0, 0); this.context.stroke(); this.context.fill(); } ``` 封装完成的组件完整示例代码: ```arkts import { Point } from "@ohos.UiTest" @ComponentV2 export struct XBorderRadiusButtonBackground { @Param topRadius: number = 20 @Param bottomRadius: number = 20 @Param activeColor: ResourceStr = '#ffffffff' @Param inactiveColor: ResourceStr = '#ff8a8a8a' @Param action: () => void = () => { } private settings: RenderingContextSettings = new RenderingContextSettings(true) private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) @Param active: boolean = true @Monitor('topRadius','bottomRadius','activeColor','inactiveColor','active') drawCanvas() { this.context.clearRect(0, 0, this.context.width, this.context.height); this.context.lineWidth = 0; this.context.fillStyle = (this.active ? this.activeColor : this.inactiveColor) as string; this.context.strokeStyle = (this.active ? this.activeColor : this.inactiveColor) as string; if (this.topRadius >= 0 && this.bottomRadius < 0) { // _______ // / \ // | | // ___/ \___ //圆心1 let p1: Point = { x: Math.abs(this.bottomRadius) + this.topRadius, y: this.topRadius } //圆心2 let p2: Point = { x: this.context.width - Math.abs(this.bottomRadius) - this.topRadius, y: this.topRadius } //圆心3 let p3: Point = { x: 0, y: this.context.height - Math.abs(this.bottomRadius) }; //圆心4 let p4: Point = { x: this.context.width, y: this.context.height - Math.abs(this.bottomRadius) } this.context.moveTo(0, this.context.height); this.context.arc(p3.x, p3.y, Math.abs(this.bottomRadius), Math.PI / 2, 0, true); this.context.lineTo(p1.x - this.topRadius, p1.y); this.context.arc(p1.x, p1.y, this.topRadius, Math.PI, -Math.PI / 2); this.context.lineTo(p2.x, p2.y - this.topRadius); this.context.arc(p2.x, p2.y, this.topRadius, -Math.PI / 2, 0); this.context.lineTo(p4.x - Math.abs(this.bottomRadius), p4.y); this.context.arc(p4.x, p4.y, Math.abs(this.bottomRadius), Math.PI, Math.PI / 2, true); this.context.stroke(); this.context.fill(); } else if (this.topRadius < 0 && this.bottomRadius >= 0) { //圆心1 let p1: Point = { x: 0, y: Math.abs(this.topRadius) } //圆心2 let p2: Point = { x: this.context.width, y: Math.abs(this.topRadius) } //圆心3 let p3: Point = { x: this.bottomRadius + Math.abs(this.topRadius), y: this.context.height - this.bottomRadius }; //圆心4 let p4: Point = { x: this.context.width - this.bottomRadius - Math.abs(this.topRadius), y: this.context.height - this.bottomRadius } this.context.moveTo(0, 0); this.context.arc(p1.x, p1.y, Math.abs(this.topRadius), -Math.PI / 2, 0); this.context.lineTo(Math.abs(this.topRadius), this.context.height - this.bottomRadius); this.context.arc(p3.x, p3.y, this.bottomRadius, Math.PI, Math.PI / 2, true); this.context.lineTo(p4.x, this.context.height); this.context.arc(p4.x, p4.y, this.bottomRadius, Math.PI / 2, 0, true); this.context.lineTo(p2.x - Math.abs(this.topRadius), p2.y); this.context.arc(p2.x, p2.y, Math.abs(this.topRadius), Math.PI, -Math.PI / 2); this.context.lineTo(0, 0); this.context.stroke(); this.context.fill(); } } build() { if ((this.topRadius >= 0 && this.bottomRadius >= 0) || (this.topRadius < 0 && this.bottomRadius < 0)) { Column() .height('100%') .width('100%') .borderRadius(Math.abs(this.topRadius + this.bottomRadius) / 2) .backgroundColor(this.active ? this.activeColor : this.inactiveColor) .onClick(() => { this.action(); }) } else { Canvas(this.context).height('100%').width('100%') .onReady(() => { this.drawCanvas(); }) } } } ```