如需自定义共享元素过渡动画的运行方式,可以使用几个参数来更改共享元素的过渡方式。
动画规范
如需更改用于大小和位置移动的动画规范,您可以在 Modifier.sharedElement()
上指定不同的 boundsTransform
参数。这样可提供初始 Rect
位置和目标 Rect
位置。
例如,如需使上例中的文本随弧形运动而移动,请指定 boundsTransform
参数以使用 keyframes
规范:
val textBoundsTransform = BoundsTransform { initialBounds, targetBounds -> keyframes { durationMillis = boundsAnimationDurationMillis initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing targetBounds at boundsAnimationDurationMillis } } Text( "Cupcake", fontSize = 28.sp, modifier = Modifier.sharedBounds( rememberSharedContentState(key = "title"), animatedVisibilityScope = animatedVisibilityScope, boundsTransform = textBoundsTransform ) )
您可以使用任何 AnimationSpec
。此示例使用了 keyframes
规范。
“调整大小”模式
在两个共享边界之间添加动画效果时,可以将 resizeMode
参数设置为 RemeasureToBounds
或 ScaleToBounds
。此参数确定共享元素如何在两种状态之间转换。ScaleToBounds
首先会使用先行(或目标)约束条件测量子布局。然后,系统会缩放子项的稳定布局,以适应共享边界。ScaleToBounds
可以视为状态之间的“图形缩放”。
而 RemeasureToBounds
会根据目标尺寸,通过固定的动画约束条件重新测量并重新布局 sharedBounds
的子布局。边界大小变化(可能是每一帧的变化)都会触发重新测量。
对于 Text
可组合项,建议使用 ScaleToBounds
,因为它可以避免对文本进行重新布局和重排到不同的行。对于宽高比不同的边界,以及如果您希望两个共享元素之间具有流畅的连续性,建议使用 RemeasureToBounds
。
以下示例展示了这两种调整大小模式之间的区别:
|
|
---|---|
跳至最终布局
默认情况下,在两个布局之间转换时,布局尺寸会在其起始状态和最终状态之间以动画形式显示。在为文本等内容添加动画效果时,这可能是不希望出现的行为。
以下示例展示了说明文本“Lorem Ipsum”以两种不同的方式进入屏幕的方式。第一个示例是文本在容器变大时自动重排;第二个示例是文本在扩大时不重排。添加 Modifier.skipToLookaheadSize()
可防止自动重排。
没有 Modifier.skipToLookahead() - 请注意“Lorem Ipsum”文本重排 |
Modifier.skipToLookahead() - 请注意“Lorem Ipsum”文本在动画开始时保持最终状态 |
---|---|
剪辑和叠加
在 Compose 中创建共享元素时需要注意的一个重要概念是,为了让这些元素在不同可组合项之间共享,当过渡开始与其在目的地中的匹配时,可组合项的渲染会提升为层叠加层。这样做的效果是它会转义父级的边界及其层转换(例如 alpha 和 scale)。
该元素将渲染在其他非共享界面元素之上,过渡完成后,该元素将从叠加层拖放到自己的 DrawScope
。
如需将共享元素裁剪为形状,请使用标准 Modifier.clip()
函数。请将其放在 sharedElement()
之后:
Image( painter = painterResource(id = R.drawable.cupcake), contentDescription = "Cupcake", modifier = Modifier .size(100.dp) .sharedElement( rememberSharedContentState(key = "image"), animatedVisibilityScope = this@AnimatedContent ) .clip(RoundedCornerShape(16.dp)), contentScale = ContentScale.Crop )
如果您需要确保共享元素绝不会在父级容器之外呈现,可以对 sharedElement()
设置 clipInOverlayDuringTransition
。默认情况下,对于嵌套的共享边界,clipInOverlayDuringTransition
会使用父级 sharedBounds()
的裁剪路径。
如需支持在共享元素过渡期间使特定的界面元素(例如底部栏或悬浮操作按钮)始终位于顶部,请使用 Modifier.renderInSharedTransitionScopeOverlay()
。默认情况下,此修饰符会在共享过渡处于活动状态期间将内容保留在叠加层中。
例如,在 Jetsnack 中,需要将 BottomAppBar
放置在共享元素之上,直到屏幕不可见。向可组合项添加修饰符会使其保持升高。
不含 |
通过 |
---|---|
有时,您可能希望非共享可组合项在过渡前呈现动画效果,并保持在其他可组合项之上。在这种情况下,请使用 renderInSharedTransitionScopeOverlay().animateEnterExit()
在共享元素过渡运行时为可组合项添加动画效果:
JetsnackBottomBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f, ) .animateEnterExit( enter = fadeIn() + slideInVertically { it }, exit = fadeOut() + slideOutVertically { it } ) )
在极少数情况下,如果您不希望共享元素在叠加层中渲染,可以将 sharedElement()
上的 renderInOverlayDuringTransition
设置为 false。
通知同级布局有关共享元素大小的变化
默认情况下,sharedBounds()
和 sharedElement()
不会在布局转换时通知父级容器任何大小变化。
若要在父级容器转换时将大小更改传播到父级容器,请将 placeHolderSize
参数更改为 PlaceHolderSize.animatedSize
。这样做会导致内容放大或缩小。布局中的所有其他项都会响应更改。
|
(请注意,随着某一项内容的增加,列表中的其他项是如何向下移动的) |
---|---|