如何用Jetpack Compose的MotionLayout实现圆形单侧拉伸移动动画?
解决Jetpack Compose MotionLayout实现局部拉伸移动圆形动画的问题
要实现圆形先向左拉伸左侧部分,再让右侧跟进并恢复完整圆形的动画,你需要拆分圆形组件并分别控制左右部分的动画轨迹,避免使用整体scaleX或translationX导致的不符合预期的效果。以下是具体实现方案:
核心思路
将完整圆形拆分为左、右两个半圆组件,通过MotionLayout分别定义两个组件的关键帧动画:
- 第一阶段(0-300ms):左半圆向左拉伸并偏移,右半圆保持静止
- 第二阶段(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%进度:两个组件稳定在最终位置,确保动画平滑收尾
- 0-30%进度(0-180ms):左半圆
- 位置调整:根据实际布局需求修改
ConstraintSets中的start偏移量,适配你的UI场景
内容的提问来源于stack exchange,提问作者Silverbaq
相关产品推荐
相关产品推荐

