Jetpack Compose:如何绘制这样的路径/线

5

我想使用画布(canvas)实现类似下面图片的效果。如何实现这样的效果?

Enter image description here

有没有一些可以查阅的参考资料?


1
也许这篇文章能帮到你。 - ngalashev
2个回答

17

你可以使用Path和cubicTo来绘制它。

path.cubicTo(x1 = x1, y1 = y1, x2 = x2, y2 = y2, x3 = x3, y3 = y3)

要创建虚线效果,您应该使用:

drawPath(
            color = Color.Green,
            path = path,
            style = Stroke(
                width = 3.dp.toPx(),
                pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
            )
        )

通过以下示例,我认为可以很容易地理解如何通过更改起始点(x0,y0),结束点(x3,y3)和控制点(x1,y1)和(x2,y2)来改变立方体。

输入图像描述

@Composable
fun DrawCubic() {

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
    ) {

        val density = LocalDensity.current.density

        val configuration = LocalConfiguration.current
        val screenWidth = configuration.screenWidthDp.dp

        val screenWidthInPx = screenWidth.value * density

        // (x0, y0) is initial coordinate where path is moved with path.moveTo(x0,y0)
        var x0 by remember { mutableStateOf(0f) }
        var y0 by remember { mutableStateOf(0f) }

        /*
        Adds a cubic bezier segment that curves from the current point(x0,y0) to the
        given point (x3, y3), using the control points (x1, y1) and (x2, y2).
     */
        var x1 by remember { mutableStateOf(0f) }
        var y1 by remember { mutableStateOf(screenWidthInPx) }
        var x2 by remember { mutableStateOf(screenWidthInPx/2) }
        var y2 by remember { mutableStateOf(0f) }

        var x3 by remember { mutableStateOf(screenWidthInPx) }
        var y3 by remember { mutableStateOf(screenWidthInPx/2) }

        val path = remember { Path() }
        Canvas(
            modifier = Modifier
                .padding(8.dp)
                .shadow(1.dp)
                .background(Color.White)
                .size(screenWidth, screenWidth/2)
        ) {
            path.reset()
            path.moveTo(x0, y0)
            path.cubicTo(x1 = x1, y1 = y1, x2 = x2, y2 = y2, x3 = x3, y3 = y3)


            drawPath(
                color = Color.Green,
                path = path,
                style = Stroke(
                    width = 3.dp.toPx(),
                    pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
                )
            )

            // Draw Control Points on screen
            drawPoints(
                listOf(Offset(x1, y1), Offset(x2, y2)),
                color = Color.Green,
                pointMode = PointMode.Points,
                cap = StrokeCap.Round,
                strokeWidth = 40f
            )
        }

        Column(modifier = Modifier.padding(horizontal = 20.dp)) {

            Text(text = "X0: ${x0.roundToInt()}")
            Slider(
                value = x0,
                onValueChange = { x0 = it },
                valueRange = 0f..screenWidthInPx,
            )

            Text(text = "Y0: ${y0.roundToInt()}")
            Slider(
                value = y0,
                onValueChange = { y0 = it },
                valueRange = 0f..screenWidthInPx,
            )

            Text(text = "X1: ${x1.roundToInt()}")
            Slider(
                value = x1,
                onValueChange = { x1 = it },
                valueRange = 0f..screenWidthInPx,
            )

            Text(text = "Y1: ${y1.roundToInt()}")
            Slider(
                value = y1,
                onValueChange = { y1 = it },
                valueRange = 0f..screenWidthInPx,
            )

            Text(text = "X2: ${x2.roundToInt()}")
            Slider(
                value = x2,
                onValueChange = { x2 = it },
                valueRange = 0f..screenWidthInPx,
            )

            Text(text = "Y2: ${y2.roundToInt()}")
            Slider(
                value = y2,
                onValueChange = { y2 = it },
                valueRange = 0f..screenWidthInPx,
            )

            Text(text = "X3: ${x3.roundToInt()}")
            Slider(
                value = x3,
                onValueChange = { x3 = it },
                valueRange = 0f..screenWidthInPx,
            )

            Text(text = "Y3: ${y3.roundToInt()}")
            Slider(
                value = y3,
                onValueChange = { y3 = it },
                valueRange = 0f..screenWidthInPx,
            )
        }
    }
}

有关四边形、立方体、路径、混合模式和画布的更多信息,请参阅此教程


1
你可以用三次方程式来绘制OP所要求的路径。对于箭头路径,你可以在(x3,y3)的位置添加另一条路径。正如我在答案中提到的,这个答案可以帮助你了解当你改变三次方程式的控制点和起始点、结束点时,它是如何变化的。 - Thracian
我的错,我没有意识到绘制立方体的实际代码几乎与我所示范的方法相同。可以说是更好了。由于其余代码库让我感到困惑,所以我认为只有十行代码用于渲染立方体...这是懒惰的阅读,抱歉。 - Richard Onslow Roper
1
滑块用于演示控制点如何改变三次贝塞尔曲线。 - Thracian
1
是的,你说得对。OP要求一个参考资料,所以我不仅发布了如何绘制贝塞尔曲线的信息,还说明了如何通过设置点来改变曲线。没有参考资料,贝塞尔曲线可能会很复杂,这就是为什么答案有点长的原因。此外,我绘制控制点以可视化它们实际上在何处,当您更改曲线时。祝大家有美好的一天。✌️ - Thracian
1
太棒了!你有一个多么漂亮的立方体啊! - StayCool
显示剩余4条评论

5

作为对于 Thracian的代码 的一般替代方案,该代码使用编程方法实时构建路径来绘制复杂形状/向量。这是一种更轻松的方法:

val pathData = "M 60,60 L 60,0 L 50,10 L 60,0 L 70,10"

Canvas() {
  drawPath(
    path = PathParser.createPathFromPathData(pathData)).asComposePath()
  )
}

例如,这可以在屏幕上绘制箭头。你需要的是线/向量的pathData,然后将其赋值给pathData变量,就像上面所示的那样。

此外,请记住这里使用的PathParserandroidx.graphics包中的一个,而不是Compose Path Parser。


如果您有关于Canvas、路径、三次或四次贝塞尔曲线的更多参考资料,请分享给大家。这将对所有人都有很大帮助。关于Canvas的好资源真的很难找到。 - Thracian
请随意在此发表意见:https://dev59.com/zsLra4cB1Zd3GeqPLYVr - Richard Onslow Roper
“Compsose”是什么意思?您是不是想说“Compose”? - Peter Mortensen
不,Pete。我说的就是我的意思。我在谈论Compsose。它是一个...一个框架库,由这里的Thracian博士建立。 - Richard Onslow Roper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接