如何在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
相关产品推荐
相关产品推荐

