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

如何用Jetpack Compose的MotionLayout实现圆形单侧拉伸移动动画?

解决Jetpack Compose MotionLayout实现局部拉伸移动圆形动画的问题

要实现圆形先向左拉伸左侧部分,再让右侧跟进并恢复完整圆形的动画,你需要拆分圆形组件并分别控制左右部分的动画轨迹,避免使用整体scaleX或translationX导致的不符合预期的效果。以下是具体实现方案:

核心思路

将完整圆形拆分为左、右两个半圆组件,通过MotionLayout分别定义两个组件的关键帧动画:

  1. 第一阶段(0-300ms):左半圆向左拉伸并偏移,右半圆保持静止
  2. 第二阶段(300-600ms):左半圆恢复原宽度并调整到最终位置左侧,右半圆向左跟进拼合为完整圆形

修改后的Composable代码

@OptIn(ExperimentalMotionApi::class)
@Preview
@Composable
fun AnimationScreen() {
    val context = LocalContext.current
    val motionScene = remember {
        context.resources.openRawResource(R.raw.motion_scene).readBytes().decodeToString()
    }
    var progress by remember { mutableStateOf(0f) }

    Column {
        MotionLayout(
            motionScene = MotionScene(content = motionScene),
            progress = progress,
            modifier = Modifier
                .fillMaxWidth()
                .height(200.dp)
        ) {
            // 左半圆:仅保留左侧圆角
            Box(
                modifier = Modifier
                    .size(100.dp)
                    .clip(RoundedCornerShape(topStart = 50.dp, bottomStart = 50.dp))
                    .background(Color.Red)
                    .layoutId("left_circle")
            )
            // 右半圆:仅保留右侧圆角
            Box(
                modifier = Modifier
                    .size(100.dp)
                    .clip(RoundedCornerShape(topEnd = 50.dp, bottomEnd = 50.dp))
                    .background(Color.Red)
                    .layoutId("right_circle")
            )
        }

        Spacer(Modifier.height(32.dp))

        Slider(
            value = progress,
            onValueChange = { progress = it }
        )
    }
}

修改后的motion_scene.json5代码

{
  ConstraintSets: {
    start: {
      left_circle: {
        width: 50,
        height: 100,
        start: ['parent', 'start', 200], // 初始位置:距离父布局左侧200px
        top: ['parent', 'top', 50],
      },
      right_circle: {
        width: 50,
        height: 100,
        start: ['left_circle', 'end', 0], // 右半圆紧贴左半圆右侧
        top: ['left_circle', 'top', 0],
      }
    },
    end: {
      left_circle: {
        width: 50,
        height: 100,
        start: ['parent', 'start', 100], // 最终位置:距离父布局左侧100px
        top: ['parent', 'top', 50],
      },
      right_circle: {
        width: 50,
        height: 100,
        start: ['left_circle', 'end', 0],
        top: ['left_circle', 'top', 0],
      }
    }
  },
  Transitions: {
    default: {
      from: 'start',
      to: 'end',
      duration: 600, // 总时长600ms,分两个300ms阶段
      KeyFrames: {
        KeyAttributes: [
          // 左半圆动画:先拉伸左移,再恢复宽度到最终位置
          {
            target: ['left_circle'],
            frames: [0, 30, 60, 100],
            scaleX: [1, 2, 1, 1],
            translationX: [0, -50, -50, 0]
          },
          // 右半圆动画:先静止,再左移跟进到最终位置
          {
            target: ['right_circle'],
            frames: [0, 30, 60, 100],
            translationX: [0, 0, -100, -100]
          }
        ]
      }
    }
  }
}

关键细节说明

  • 组件拆分:将圆形拆分为两个半圆,通过clip修饰符分别保留左右圆角,拼合后呈现完整圆形
  • 动画阶段控制:
    • 0-30%进度(0-180ms):左半圆scaleX从1变为2(向左拉伸),同时translationX左移50px,模拟左侧拉伸效果;右半圆保持不动
    • 30%-60%进度(180-360ms):左半圆scaleX恢复为1,调整位置到最终点左侧;右半圆translationX左移100px,跟进到左半圆右侧,重新组成完整圆形
    • 60%-100%进度:两个组件稳定在最终位置,确保动画平滑收尾
  • 位置调整:根据实际布局需求修改ConstraintSets中的start偏移量,适配你的UI场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 14:10:27