You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Jetpack Compose中如何计算带圆角边缘的圆弧的边缘点坐标

Jetpack Compose中如何计算带圆角边缘的圆弧的边缘点坐标

嘿,我来帮你理清楚这个问题!你已经搭建好了基础的弧形结构,现在要解决的就是计算边缘端点坐标,然后画出平滑的圆角。首先得记住Jetpack Compose Canvas的坐标系规则:原点在左上角,x向右为正,y向下为正,角度以顺时针为正方向(0度指向右侧),这对坐标转换至关重要。

第一步:计算核心端点的坐标

我们可以利用极坐标转笛卡尔坐标的公式来计算外弧和内弧的端点:
x = 圆心x + 半径 * cos(角度弧度)
y = 圆心y + 半径 * sin(角度弧度)

先把角度转成弧度,然后代入你的参数计算:

import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.toRadians

// 你的基础参数
val size = Size(500, 250)
val xOrigin = size.width / 2f
val height = size.height
val stroke = 60f // 建议用Float类型避免精度问题
val outerRadius = size.width / 2f
val innerRadius = outerRadius - stroke
val startAngle = -180f
val sweep = 45f
val center = Offset(xOrigin, height)

// 转换角度为弧度
val startAngleRad = startAngle.toDouble().toRadians().toFloat()
val endAngle = startAngle + sweep
val endAngleRad = endAngle.toDouble().toRadians().toFloat()

// 外弧的两个端点
val outerStart = Offset(
    x = center.x + outerRadius * cos(startAngleRad),
    y = center.y + outerRadius * sin(startAngleRad)
) // 对应你图中左侧的外端点
val outerEnd = Offset(
    x = center.x + outerRadius * cos(endAngleRad),
    y = center.y + outerRadius * sin(endAngleRad)
) // 对应你图中斜角的外端点

// 内弧的两个端点
val innerStart = Offset(
    x = center.x + innerRadius * cos(endAngleRad),
    y = center.y + innerRadius * sin(endAngleRad)
) // 对应斜角的内端点
val innerEnd = Offset(
    x = center.x + innerRadius * cos(startAngleRad),
    y = center.y + innerRadius * sin(startAngleRad)
) // 对应左侧的内端点

计算出来的结果和预期一致:

  • outerStart(xOrigin - outerRadius, height)(底部左侧外边缘点)
  • outerEnd(xOrigin - outerRadius*0.7071f, height - outerRadius*0.7071f)(左上方外边缘点)
  • innerStart(xOrigin - innerRadius*0.7071f, height - innerRadius*0.7071f)(左上方内边缘点)
  • innerEnd(xOrigin - innerRadius, height)(底部左侧内边缘点)

第二步:计算圆角的圆心与路径

要实现平滑的圆角,建议把圆角半径设为stroke/2(也就是30f),这样圆角和弧形的宽度适配性最好。

左侧圆角(连接outerStart和innerEnd)

这个圆角是水平方向的,圆心在outerStart右侧30f的位置:

val cornerRadius = stroke / 2f
val leftCornerCenter = Offset(outerStart.x + cornerRadius, outerStart.y)

画这个圆角时,从outerStart开始,用arcTo画一个180度的圆弧到innerEnd

path.arcTo(
    rect = Rect(
        left = leftCornerCenter.x - cornerRadius,
        top = leftCornerCenter.y - cornerRadius,
        right = leftCornerCenter.x + cornerRadius,
        bottom = leftCornerCenter.y + cornerRadius
    ),
    startAngle = 180f, // 从左侧开始
    sweepAngle = 180f, // 顺时针转180度到右侧
    forceMoveTo = false
)

斜角圆角(连接outerEnd和innerStart)

这个圆角的圆心需要沿着圆心到outerEnd的方向,往内移动cornerRadius的距离(保证同时和外弧、内弧相切):

val rightCornerCenter = Offset(
    x = center.x + (outerRadius - cornerRadius) * cos(endAngleRad),
    y = center.y + (outerRadius - cornerRadius) * sin(endAngleRad)
)

画这个圆角时,从outerEnd开始,画一个180度的圆弧到innerStart

path.arcTo(
    rect = Rect(
        left = rightCornerCenter.x - cornerRadius,
        top = rightCornerCenter.y - cornerRadius,
        right = rightCornerCenter.x + cornerRadius,
        bottom = rightCornerCenter.y + cornerRadius
    ),
    startAngle = endAngle + 90f, // 外弧的切线方向
    sweepAngle = 180f,
    forceMoveTo = false
)

第三步:拼接完整路径

把外弧、斜角圆角、内弧、左侧圆角依次连接起来,形成完整的带圆角的弧形:

val path = Path().apply {
    // 移动到外弧起点
    moveTo(outerStart.x, outerStart.y)
    // 画外弧
    arcTo(
        rect = Rect(
            left = center.x - outerRadius,
            top = center.y - outerRadius,
            right = center.x + outerRadius,
            bottom = center.y + outerRadius
        ),
        startAngle = startAngle,
        sweepAngle = sweep,
        forceMoveTo = false
    )
    // 画斜角圆角
    arcTo(
        rect = Rect(
            left = rightCornerCenter.x - cornerRadius,
            top = rightCornerCenter.y - cornerRadius,
            right = rightCornerCenter.x + cornerRadius,
            bottom = rightCornerCenter.y + cornerRadius
        ),
        startAngle = endAngle + 90f,
        sweepAngle = 180f,
        forceMoveTo = false
    )
    // 画内弧
    arcTo(
        rect = Rect(
            left = center.x - innerRadius,
            top = center.y - innerRadius,
            right = center.x + innerRadius,
            bottom = center.y + innerRadius
        ),
        startAngle = endAngle,
        sweepAngle = -sweep,
        forceMoveTo = false
    )
    // 画左侧圆角,闭合路径
    arcTo(
        rect = Rect(
            left = leftCornerCenter.x - cornerRadius,
            top = leftCornerCenter.y - cornerRadius,
            right = leftCornerCenter.x + cornerRadius,
            bottom = leftCornerCenter.y + cornerRadius
        ),
        startAngle = 180f,
        sweepAngle = 180f,
        forceMoveTo = false
    )
    close()
}

这样就能画出一个边缘带平滑圆角的弧形了!

备注:内容来源于stack exchange,提问作者Abhi

火山引擎 最新活动