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




