如果更改为新动画,UpdateTransition 动画会保持其 运行 速度吗?
Will UpdateTransition animation maintain its running velocity when if being changed to new animation?
我使用 AnimationAsState
、Animatable
和 UpdateTransition
创建了相同的动画并进行了尝试。
所有动画都使用向前或向后移动。
tween(
durationMillis = 3000,
easing = LinearOutSlowInEasing
)
下面我们可以看到
如果动画执行到一半,我再次点击按钮,我会假设 运行 动画速度保持不变并传递给后续动画。
这似乎适用于 AnimateAsState
和 Animatable
。然而,令我惊讶的是, UpdateTransition
似乎没有那样做。我想它已经恢复到默认的动画持续时间(运行得非常快)。
我们可以看到如下。
我的问题是,是吗
- 预计如果
UpdateTransition
被后续动画中途取消,它不会保留 运行 动画规范?
- 我的代码遗漏了什么(我在下面分享了整个代码)?
- 这是一个
UpdateTransition
错误,需要报告给 Google?
上述动画的完整工作代码如下
import androidx.compose.animation.core.*
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
fun Combination() {
var enabled by remember { mutableStateOf(false) }
val dbAnimateAsState: Dp by animateDpAsState(
targetValue = switch(enabled),
animationSpec = animationSpec()
)
val dbAnimatable = remember { Animatable(0.dp) }
val transition = updateTransition(enabled, label = "")
val dbTransition by transition.animateDp(
transitionSpec = { animationSpec() }, label = "") {
switch(it)
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("AnimateAsState")
animateBoxHorizontal(dbAnimateAsState)
Text("Animatable")
animateBoxHorizontal(dbAnimatable.value)
Text("UpdateTransition")
animateBoxHorizontal(dbTransition)
Button(onClick = { enabled = !enabled }) {
Text("Click Me")
}
}
LaunchedEffect(key1 = enabled) {
dbAnimatable.animateTo(
targetValue = switch(enabled),
animationSpec = animationSpec()
)
}
}
private fun animationSpec(): TweenSpec<Dp> =
tween(
durationMillis = 3000,
easing = LinearOutSlowInEasing
)
private fun switch(enabled: Boolean) = if (enabled) 268.dp else 0.dp
fun Animatable(initialValue: Dp) = Animatable(
initialValue,
DpToVector,
)
private val DpToVector: TwoWayConverter<Dp, AnimationVector1D> =
TwoWayConverter({ AnimationVector1D(it.value) }, { it.value.dp })
@Composable
private fun animateBoxHorizontal(dbAnimateAsState: Dp) {
Box(
modifier = Modifier
.height(32.dp)
.width(300.dp)
.background(Color.Yellow)
) {
Box(
modifier = Modifier
.offset(dbAnimateAsState, 0.dp)
.size(32.dp)
.background(Color.Red)
)
}
Spacer(modifier = Modifier.height(16.dp))
}
注意:更新信息
如果我从 tweenSpec
更改为 springSpec
即
tween(
durationMillis = 3000,
easing = LinearOutSlowInEasing
)
至
spring(stiffness =20f, dampingRatio = 0.25f)
然后 updateTransition
照常工作,其中 Spring 速度和动画在我们用新的动画打断时保持并连续。
updateTransition
仍然保持速度——如果你能减慢它可能会更明显。它在中断时切换到 spring
,而 animateAsState
和 Animatable
使用您提供的 AnimationSpec
。回退 spring 可能有点过于僵硬,因此它被认为是一个非常快速的变化。
此设计的目的是允许 Transition
在使用不同类型的 AnimationSpec
时适应中断。例如,从状态 A 到状态 B,您可能使用 keyFrames
,从状态 B 到状态 C 或者可能使用 snap()
(因为状态 B 和 C 中的值相同) .因此,当您以 C 作为新目的地从 A -> B 中断动画时,keyframes
和 snap
都会看起来很奇怪。这就是为什么当 Transition
被打断时我们回退到 spring
。
请注意,如果您已在 Transition
中为动画使用 spring
,则 spring 将用于处理该动画中的中断,因为它与您的动画更相关。这就是为什么当您向上面代码片段中的过渡提供 low-stiffness spring 时,您会看到动画的“路线修正”要慢得多。
我们确实计划支持中断处理的自定义,您可以在其中指定中断时使用的 AnimationSpec。计划将其作为未来的工作。如果您对如何处理中断有任何具体要求,或者回退 spring 应该慢一点,请随时提交功能请求。 :)
经过更多调查,显然,这是设计使然。
如果我们检查 Google 转换 class 代码 class Transition<S> @PublishedApi internal constructor
我们可以看到
val spec = if (isInterrupted) {
// When interrupted, use the default spring, unless the spec is also a spring.
if (animationSpec is SpringSpec<*>) animationSpec else interruptionSpec
} else {
animationSpec
}
和
interruptionSpec = spring(visibilityThreshold = visibilityThreshold)
因此 interruptionSpec
设置为 Spring
,如果用户提供 Spring,它会被保留。如果不是,它将被设置为 spring(visibilityThreshold = visibilityThreshold)
.
我使用 AnimationAsState
、Animatable
和 UpdateTransition
创建了相同的动画并进行了尝试。
所有动画都使用向前或向后移动。
tween(
durationMillis = 3000,
easing = LinearOutSlowInEasing
)
下面我们可以看到
如果动画执行到一半,我再次点击按钮,我会假设 运行 动画速度保持不变并传递给后续动画。
这似乎适用于 AnimateAsState
和 Animatable
。然而,令我惊讶的是, UpdateTransition
似乎没有那样做。我想它已经恢复到默认的动画持续时间(运行得非常快)。
我们可以看到如下。
我的问题是,是吗
- 预计如果
UpdateTransition
被后续动画中途取消,它不会保留 运行 动画规范? - 我的代码遗漏了什么(我在下面分享了整个代码)?
- 这是一个
UpdateTransition
错误,需要报告给 Google?
上述动画的完整工作代码如下
import androidx.compose.animation.core.*
import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
fun Combination() {
var enabled by remember { mutableStateOf(false) }
val dbAnimateAsState: Dp by animateDpAsState(
targetValue = switch(enabled),
animationSpec = animationSpec()
)
val dbAnimatable = remember { Animatable(0.dp) }
val transition = updateTransition(enabled, label = "")
val dbTransition by transition.animateDp(
transitionSpec = { animationSpec() }, label = "") {
switch(it)
}
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("AnimateAsState")
animateBoxHorizontal(dbAnimateAsState)
Text("Animatable")
animateBoxHorizontal(dbAnimatable.value)
Text("UpdateTransition")
animateBoxHorizontal(dbTransition)
Button(onClick = { enabled = !enabled }) {
Text("Click Me")
}
}
LaunchedEffect(key1 = enabled) {
dbAnimatable.animateTo(
targetValue = switch(enabled),
animationSpec = animationSpec()
)
}
}
private fun animationSpec(): TweenSpec<Dp> =
tween(
durationMillis = 3000,
easing = LinearOutSlowInEasing
)
private fun switch(enabled: Boolean) = if (enabled) 268.dp else 0.dp
fun Animatable(initialValue: Dp) = Animatable(
initialValue,
DpToVector,
)
private val DpToVector: TwoWayConverter<Dp, AnimationVector1D> =
TwoWayConverter({ AnimationVector1D(it.value) }, { it.value.dp })
@Composable
private fun animateBoxHorizontal(dbAnimateAsState: Dp) {
Box(
modifier = Modifier
.height(32.dp)
.width(300.dp)
.background(Color.Yellow)
) {
Box(
modifier = Modifier
.offset(dbAnimateAsState, 0.dp)
.size(32.dp)
.background(Color.Red)
)
}
Spacer(modifier = Modifier.height(16.dp))
}
注意:更新信息
如果我从 tweenSpec
更改为 springSpec
即
tween(
durationMillis = 3000,
easing = LinearOutSlowInEasing
)
至
spring(stiffness =20f, dampingRatio = 0.25f)
然后 updateTransition
照常工作,其中 Spring 速度和动画在我们用新的动画打断时保持并连续。
updateTransition
仍然保持速度——如果你能减慢它可能会更明显。它在中断时切换到 spring
,而 animateAsState
和 Animatable
使用您提供的 AnimationSpec
。回退 spring 可能有点过于僵硬,因此它被认为是一个非常快速的变化。
此设计的目的是允许 Transition
在使用不同类型的 AnimationSpec
时适应中断。例如,从状态 A 到状态 B,您可能使用 keyFrames
,从状态 B 到状态 C 或者可能使用 snap()
(因为状态 B 和 C 中的值相同) .因此,当您以 C 作为新目的地从 A -> B 中断动画时,keyframes
和 snap
都会看起来很奇怪。这就是为什么当 Transition
被打断时我们回退到 spring
。
请注意,如果您已在 Transition
中为动画使用 spring
,则 spring 将用于处理该动画中的中断,因为它与您的动画更相关。这就是为什么当您向上面代码片段中的过渡提供 low-stiffness spring 时,您会看到动画的“路线修正”要慢得多。
我们确实计划支持中断处理的自定义,您可以在其中指定中断时使用的 AnimationSpec。计划将其作为未来的工作。如果您对如何处理中断有任何具体要求,或者回退 spring 应该慢一点,请随时提交功能请求。 :)
经过更多调查,显然,这是设计使然。
如果我们检查 Google 转换 class 代码 class Transition<S> @PublishedApi internal constructor
我们可以看到
val spec = if (isInterrupted) {
// When interrupted, use the default spring, unless the spec is also a spring.
if (animationSpec is SpringSpec<*>) animationSpec else interruptionSpec
} else {
animationSpec
}
和
interruptionSpec = spring(visibilityThreshold = visibilityThreshold)
因此 interruptionSpec
设置为 Spring
,如果用户提供 Spring,它会被保留。如果不是,它将被设置为 spring(visibilityThreshold = visibilityThreshold)
.