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

如何在Jetpack Compose Canvas中实现drawPath路径绘制动画?

问题描述

我已经通过path.cubicTo()方法绘制出一个心形,代码如下:

Canvas(modifier = Modifier.size(100.dp)) {
    val path = Path()
    path.moveTo(0f, size.height / 3)

    path.cubicTo(0f, 0f, size.width / 2, 0f, size.width / 2, size.height / 3)
    path.cubicTo(size.width / 2, 0f, size.width, 0f, size.width, size.height / 3)

    path.cubicTo(
        size.width,
        size.height * 2 / 3,
        size.width / 2,
        size.height,
        size.width / 2,
        size.height
    )
    path.cubicTo(
        0f,
        size.height * 2 / 3,
        0f,
        size.height / 2,
        0f,
        size.height / 3
    )
    drawPath(color = Color.Red, path = path, style = Stroke(5f))
}

我希望实现类似路径逐步绘制的动画效果(线条从无到有,逐步勾勒出心形),请问在Android平台是否可以实现?

解决方案

完全可以实现,在Jetpack Compose里用动画进度结合PathMeasure就能搞定,具体做法如下:

  • 用animateFloatAsState生成一个从0到1的动画进度值,控制路径的绘制比例
  • 通过PathMeasure获取完整心形路径的总长度
  • 根据当前进度,截取路径的一部分来绘制

完整代码示例:

@Composable
fun AnimatedHeart() {
    // 动画进度,从0到1,时长2秒
    val progress by animateFloatAsState(
        targetValue = 1f,
        animationSpec = tween(
            durationMillis = 2000,
            easing = LinearEasing
        ),
        label = "heart path animation"
    )

    Canvas(modifier = Modifier.size(100.dp)) {
        // 先创建完整的心形路径
        val fullPath = Path().apply {
            moveTo(0f, size.height / 3)

            cubicTo(0f, 0f, size.width / 2, 0f, size.width / 2, size.height / 3)
            cubicTo(size.width / 2, 0f, size.width, 0f, size.width, size.height / 3)

            cubicTo(
                size.width,
                size.height * 2 / 3,
                size.width / 2,
                size.height,
                size.width / 2,
                size.height
            )
            cubicTo(
                0f,
                size.height * 2 / 3,
                0f,
                size.height / 2,
                0f,
                size.height / 3
            )
        }

        val pathMeasure = PathMeasure()
        pathMeasure.setPath(fullPath, false)
        val totalLength = pathMeasure.length

        // 根据进度截取部分路径
        val animatedPath = Path()
        pathMeasure.getSegment(0f, totalLength * progress, animatedPath, true)

        drawPath(
            color = Color.Red,
            path = animatedPath,
            style = Stroke(5f)
        )
    }
}

补充说明

  • 调整durationMillis的值可以改变动画快慢,数值越大动画越慢
  • getSegment的第四个参数设为true,能保证截取的路径起点正确,不会出现偏移
  • 如果需要重复播放动画,可以用rememberInfiniteTransition来实现无限循环:
@Composable
fun RepeatingAnimatedHeart() {
    val infiniteTransition = rememberInfiniteTransition(label = "infinite heart animation")
    val progress by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(2000, easing = LinearEasing),
            repeatMode = RepeatMode.Restart
        ),
        label = "heart progress"
    )

    // 后续绘制逻辑和上面的AnimatedHeart一致
}

内容的提问来源于stack exchange,提问作者Abubakr Elsadig

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:17:36