0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

進捗用のメータをCanvasで実装

0
Posted at

はじめに

今回は進捗用のメータをCanvasで実装していきます

コード

@Composable
fun ProgressMeter(
    currentStep: Int,
    modifier: Modifier = Modifier,
) {
    // 現在ステップを 0.0 - 1.0 の進捗率に変換し、ゲージ更新をアニメーションさせる部分
    val animatedProgress = animateFloatAsState(
        targetValue = currentStep.coerceIn(0, 6).toFloat() / 6f,
        animationSpec = tween(durationMillis = 600),
        label = ""
    )

    Canvas(modifier = modifier) {
        // ゲージ全体の見た目を決める基本サイズを組み立てる部分
        val strokeWidth = 12.dp.toPx()
        val centerDotRadius = 6.dp.toPx()
        val needleBaseWidth = 6.dp.toPx()
        val needleTailLength = 8.dp.toPx()
        val center = Offset(x = size.width / 2f, y = size.height - strokeWidth / 2f)
        val radius = minOf(
            size.width / 2f - strokeWidth / 2f,
            size.height - strokeWidth / 2f
        )
        val arcTopLeft = Offset(x = center.x - radius, y = center.y - radius)
        val arcSize = Size(width = radius * 2f, height = radius * 2f)
        val segmentSweepAngle = (180f - 4f * 6f) / 6f
        val clampedStep = currentStep.coerceIn(1, 6)
        val activeSegmentIndex = clampedStep - 1

        // 6 分割のメーターを描き、未達成部分と達成部分の色を描き分ける部分
        repeat(6) { index ->
            val segmentStartAngle = 180f + index * (segmentSweepAngle + 4f)
            val segmentProgress =
                (animatedProgress.value * 6f - index).coerceIn(0f, 1f)

            drawArc(
                color = Color(0xFFEBEBEB),
                startAngle = segmentStartAngle,
                sweepAngle = segmentSweepAngle,
                useCenter = false,
                topLeft = arcTopLeft,
                size = arcSize,
                style = Stroke(width = strokeWidth, cap = StrokeCap.Butt)
            )

            if (segmentProgress > 0f) {
                drawArc(
                    color = if (index == activeSegmentIndex) {
                        Color(0xFFFFB238)
                    } else {
                        Color(0xFFFFC870)
                    },
                    startAngle = segmentStartAngle,
                    sweepAngle = segmentSweepAngle * segmentProgress,
                    useCenter = false,
                    topLeft = arcTopLeft,
                    size = arcSize,
                    style = Stroke(width = strokeWidth, cap = StrokeCap.Butt)
                )
            }
        }

        // 現在位置を指す針の角度を計算し、四角形に近いポリゴンとして描画する部分
        val needleAngle = 180f + activeSegmentIndex * (segmentSweepAngle + 4f) + segmentSweepAngle / 2f
        val needleLength = radius - strokeWidth
        val tip = center + polarOffset(needleAngle, needleLength)
        val tail = center + polarOffset(needleAngle + 180f, needleTailLength)
        val leftBase = center + polarOffset(needleAngle + 90f, needleBaseWidth / 2f)
        val rightBase = center + polarOffset(needleAngle - 90f, needleBaseWidth / 2f)

        drawPath(
            path = Path().apply {
                moveTo(tail.x, tail.y)
                lineTo(leftBase.x, leftBase.y)
                lineTo(tip.x, tip.y)
                lineTo(rightBase.x, rightBase.y)
                close()
            },
            color = Color(0xFF272525)
        )

        // 針の根元に円を重ねて、メーター中心の見た目を整える部分
        drawCircle(
            color = Color(0xFF272525),
            radius = centerDotRadius,
            center = center
        )
    }
}

// 角度と長さから X/Y 座標への移動量を作り、円弧上の点を計算する補助関数
private fun polarOffset(
    angleInDegrees: Float,
    length: Float,
): Offset {
    val angleInRadians = Math.toRadians(angleInDegrees.toDouble())
    return Offset(
        x = (cos(angleInRadians) * length).toFloat(),
        y = (sin(angleInRadians) * length).toFloat()
    )
}

最後に

マックスを変更すればいろんなものに使えるので便利だと思います

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?