You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中绘制可旋转的3D立方体等3D对象?

Jetpack Compose 3D旋转立方体入门实现

实现思路

不用依赖第三方3D库,直接使用Compose原生Canvas配合图形变换、基础3D坐标投影即可实现,核心逻辑不到100行代码,上手门槛极低。

完整可运行示例代码

import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.unit.dp

// 立方体8个顶点的3D坐标,中心为原点,边长200.dp
private val cubeVertices = listOf(
    floatArrayOf(-100f, -100f, -100f),
    floatArrayOf(100f, -100f, -100f),
    floatArrayOf(100f, 100f, -100f),
    floatArrayOf(-100f, 100f, -100f),
    floatArrayOf(-100f, -100f, 100f),
    floatArrayOf(100f, -100f, 100f),
    floatArrayOf(100f, 100f, 100f),
    floatArrayOf(-100f, 100f, 100f),
)

// 立方体6个面对应的顶点索引
private val cubeFaces = listOf(
    listOf(0, 1, 2, 3), // 背面
    listOf(4, 5, 6, 7), // 正面
    listOf(0, 4, 7, 3), // 左面
    listOf(1, 5, 6, 2), // 右面
    listOf(0, 1, 5, 4), // 顶面
    listOf(3, 2, 6, 7), // 底面
)

// 每个面的填充色
private val faceColors = listOf(
    Color.Red, Color.Green, Color.Blue,
    Color.Yellow, Color.Cyan, Color.Magenta
)

@Composable
fun RotatingCube() {
    // 无限旋转动画
    val infiniteTransition = rememberInfiniteTransition(label = "cube_rotation")
    val rotationX by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(tween(4000)),
        label = "rotation_x"
    )
    val rotationY by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(tween(6000)),
        label = "rotation_y"
    )

    Canvas(modifier = Modifier.fillMaxSize()) {
        val centerX = size.width / 2
        val centerY = size.height / 2
        val focalLength = 300f // 透视焦距,数值越小透视效果越强

        withTransform({
            rotateX(rotationX, pivot = Offset(centerX, centerY))
            rotateY(rotationY, pivot = Offset(centerX, centerY))
        }) {
            cubeFaces.forEachIndexed { faceIndex, vertexIndices ->
                val path = Path()
                vertexIndices.forEachIndexed { index, vertexIndex ->
                    val (x, y, z) = cubeVertices[vertexIndex]
                    // 简单透视投影计算
                    val scale = focalLength / (focalLength + z)
                    val screenX = centerX + x * scale
                    val screenY = centerY + y * scale

                    if (index == 0) {
                        path.moveTo(screenX, screenY)
                    } else {
                        path.lineTo(screenX, screenY)
                    }
                }
                path.close()
                drawPath(path, color = faceColors[faceIndex])
            }
        }
    }
}

关键逻辑说明

  • 顶点与面定义:提前定义立方体8个顶点的三维坐标和6个面的顶点索引,避免重复计算
  • 动画实现:用rememberInfiniteTransition生成循环的X、Y轴旋转角度,实现自动旋转效果,也可以替换成触摸手势控制的状态变量实现手动旋转
  • 透视投影:通过简单的焦距公式把3D坐标转换成2D屏幕坐标,实现近大远小的3D视觉效果,调整focalLength数值可以修改透视强度
  • 绘制逻辑:遍历每个面用Path连接投影后的顶点,填充不同颜色方便区分立方体的不同面

扩展优化方向

  • 可以增加面的描边,提升立体感
  • 增加背面剔除逻辑,避免绘制被遮挡的面提升性能
  • 增加z轴排序,保证远处的面先绘制、近处的面后绘制,避免显示顺序错误

内容的提问来源于stack exchange,提问作者Nurseyit Tursunkulov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 13:36:07