版权声明
本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/16812968.html
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
前言
此篇博客以记录三角函数与圆的代码上的应用,主要就是依靠三角函数获取圆上的坐标值
获取极坐标系下的圆上的坐标值
在开发的时候需要使用这个公式,只有你把Canvas中心点移动到中央的时候。
坐标图:
如下图已知半径、切角、圆心坐标求P点坐标值
公式:
θ = angle * π / 180 px=Cx+R*cos(θ) //x坐标值 = 圆心X坐标 + 半径 * cos (θ) py=Cy+R*sin(θ) //y坐标值 = 圆心y坐标 + 半径 * cos (θ)
代码写法:
val angle = 0 val radius = 30f val centerX = 0f val centerY = 0f val px = centerX + radius * cos(angle * Math.PI / 180) val py = centerY + radius * sin(angle * Math.PI / 180)
获取竖直坐标系下的圆上的坐标值
请注意Y轴向下的值是正数增加的,而X轴向右的值是正数增加的,这里的坐标图其实我们Android开发Canvas里的坐标图,并不是第四象限。
坐标图:
如下图已知半径、切角、圆心坐标求P点坐标值
公式:
求py的加号变成了减号
θ = angle * π / 180 px=Cx+R*cos(θ) //x坐标值 = 圆心X坐标 + 半径 * cos (θ) py=Cy-R*sin(θ) //y坐标值 = 圆心y坐标 - 半径 * cos (θ)
代码写法:
val angle = 0 val radius = 30f val centerX = 100f val centerY = 100f val px = centerX + radius * cos(angle * Math.PI / 180) val py = centerY - radius * sin(angle * Math.PI / 180)
已知圆心坐标与P点坐标求θ的角度
上面的2个公式都是求圆周的P点坐标,此公式是求θ角度。在实际开发中其实很少有需要使用这个公式的需求,但是一些个别情况下会使用,比如你玩王者荣耀或者吃鸡游戏上的方向控制键。我们有需求将触控范围限制到圆内。就是需要计算当前角度,对比用户按下的P点与当前角度下的P点,有没有超出圆的范围。
公式
代码写法:
val tanX = px - centerX val tanY = centerY - py val tan = abs(tanY/tanX).toDouble() var direction = Math.toDegrees(atan(tan)).toInt() direction = if (tanY > 0) { if (tanX < 0) 180 - direction else direction } else { if (tanX >= 0) 360 - direction else 180 + direction } Log.e("zh", "角度等于 = ${direction}")
一些使用例子
画一个五角星
代码:
@Preview @Composable fun MyCanvas() { Canvas( modifier = Modifier .width(500.dp) .height(500.dp) .background(Color.White) ) { val radius = 300f val centerX = size.width / 2 val centerY = size.height / 2 //画圆 drawCircle( color = Color.Black, radius = radius, center = Offset(centerX, centerY), style = Stroke(2f) ) /** * 用三角函数计算出五角星的五个角 */ val pxArray = mutableListOf<Float>() val pyArray = mutableListOf<Float>() val textPaint = Paint() textPaint.textSize = 20f for (i in 1..5) { //360度除5,平均72度,在按照位置乘以相应的数量,因为Canvas0度并不是在0点方向所以需要补90度(90/5=18) val angle = 360 / 5 * i + 18 val px = centerX + radius * cos(angle * Math.PI / 180).toFloat() pxArray.add(px) val py = centerY - radius * sin(angle * Math.PI / 180).toFloat() pyArray.add(py) //画5个小点 drawCircle(color = Color.Red, radius = 3f, center = Offset(px, py)) //用文字辅助标记一下点的顺序与位置 drawIntoCanvas { if (i == 2){ it.nativeCanvas.drawText("点${i}", px - 35, py - 5, textPaint) } else if (i == 3 || i == 4){ it.nativeCanvas.drawText("点${i}", px - 5, py + 35, textPaint) } else{ it.nativeCanvas.drawText("点${i}", px + 5, py - 10, textPaint) } } } /** * 按照 1>3>5>2>4的位置步骤画出五角星 */ val path = Path() path.moveTo(pxArray[0], pyArray[0]) path.lineTo(pxArray[2], pyArray[2]) path.lineTo(pxArray[4], pyArray[4]) path.lineTo(pxArray[1], pyArray[1]) path.lineTo(pxArray[3], pyArray[3]) path.close() drawPath(path, color = Color.Yellow) } }
效果图:
画时钟
代码:
@Preview @Composable fun MyClock() { val hour = remember { mutableStateOf<Float>(0f) } val minute = remember { mutableStateOf<Float>(0f) } val second = remember { mutableStateOf<Float>(0f) } LaunchedEffect(key1 = "") { while (true) { val cal = Calendar.getInstance() hour.value = cal.get(Calendar.HOUR).toFloat() minute.value = cal.get(Calendar.MINUTE).toFloat() second.value = cal.get(Calendar.SECOND).toFloat() delay(1000) } } Canvas( modifier = Modifier .width(500.dp) .height(500.dp) .background(Color.White) ) { val radius = 220f val centerX = size.width / 2 val centerY = size.height / 2 //画圆心 drawCircle( color = Color.Black, radius = 5f, center = Offset(centerX, centerY), ) //画表盘圆 drawCircle( color = Color.Black, radius = radius, center = Offset(centerX, centerY), style = Stroke(2f) ) /** * 绘制时钟文字 */ val textPaint = Paint() textPaint.textAlign = Paint.Align.CENTER textPaint.textSize = 20f for (i in 1 .. 12) { val angle = 360 / 12 * i + 90 //加90度补正 val px1 = centerX + radius * cos(angle * Math.PI / 180).toFloat() val py1 = centerY - radius * sin(angle * Math.PI / 180).toFloat() //画红色小点 drawCircle(color = Color.Red, radius = 3f, center = Offset(px1, py1)) //文字的位置 val px3 = centerX + (radius * 0.8f) * cos(angle * Math.PI / 180).toFloat() //因为文字有绘制基线导致12会比6显示更高一点,这样与时钟圆盘显示起来不在同一的圆心点上,这里减少圆心Y轴坐标做到 val py3 = (centerY + 10) - (radius * 0.8f) * sin(angle * Math.PI / 180).toFloat() drawIntoCanvas { it.nativeCanvas.drawText("${if (12 - i == 0) 12 else 12 - i}", px3, py3, textPaint) } } /** * 绘制刻度 */ for (i in 1..60) { val angle = 360 / 60 * i + 48 val px1 = centerX + radius * cos(angle * Math.PI / 180).toFloat() val py1 = centerY - radius * sin(angle * Math.PI / 180).toFloat() var scaleRadius = radius * 0.95f if (i % 5 == 2) { scaleRadius = radius * 0.90f } val px2 = centerX + (scaleRadius) * cos(angle * Math.PI / 180).toFloat() val py2 = centerY - (scaleRadius) * sin(angle * Math.PI / 180).toFloat() drawLine(color = Color.Blue, Offset(px1, py1), Offset(px2, py2)) } /** * 绘制时分秒的走表 */ //计算当前小时的角度, -90是将0度位置从3点钟方向补正到0点钟方向,最后的转负数是转成顺时针 var hourAngle = -(360 / 12 * hour.value - 90) //求当前分钟的比例 val minuteProportion = 360f / 60f * minute.value / 360 //将30度乘分钟比得出当前小时需要多移动的角度 hourAngle -= (360 / 12 * minuteProportion).toInt() val hourPx = centerX + (radius*0.5f) * cos(hourAngle * Math.PI / 180).toFloat() val hourPy = centerY - (radius*0.5f) * sin(hourAngle * Math.PI / 180).toFloat() drawLine(color = Color.Blue, Offset(centerX, centerY), Offset(hourPx, hourPy)) //计算当前分钟的角度 var minuteAngle = -(360 / 60 * minute.value - 90) //求当前秒钟的比例 val secondProportion = 360f / 60f * second.value / 360 //将6度乘秒钟比得出当前分钟需要多移动的角度 minuteAngle -= (360 / 60 * secondProportion).toInt() val minutePx = centerX + (radius*0.8f) * cos(minuteAngle * Math.PI / 180).toFloat() val minutePy = centerY - (radius*0.8f) * sin(minuteAngle * Math.PI / 180).toFloat() drawLine(color = Color.Blue, Offset(centerX, centerY), Offset(minutePx, minutePy)) //计算当前秒的角度 val secondAngle = -(360 / 60 * second.value - 90) val secondPx = centerX + (radius*0.8f) * cos(secondAngle * Math.PI / 180).toFloat() val secondPy = centerY - (radius*0.8f) * sin(secondAngle * Math.PI / 180).toFloat() drawLine(color = Color.Red, Offset(centerX, centerY), Offset(secondPx, secondPy)) } }
效果图:
End
本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/16812968.html
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。