观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接: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

posted on 2022-10-21 11:41  观心静  阅读(1153)  评论(0编辑  收藏  举报