lerp函数
线性插值函数有三个参数,start、end 、 fraction ( 取值范围[0,1] ) ,返回 [start,end] 区间内 fraction 对应的值。
例如 lerp(0, 100, 0.5) => 50 。
这跟动画有什么关系呢?
看最后一行,名字都一样 ,这不就是 BaseTargetAnimation 每次屏幕刷新时取动画值嘛。
再看下 Compose 中提供了下面这些类型的 lerp 函数
Dp、Color 这些已经提供 TwoWayConverter 的类型可以直接使用动画 API ,就没必要再用 lerp 函数拓展了。
没有实现 TwoWayConverter 的类型就可以使用 lerp 函数来拓展动画功能了,特别是像 TextStyle 这种好多类型组合在一起的类型。
拓展的思路也很简单,就是对 fraction 参数做一个属性动画,函数返回值也会随着 fraction 变化而变化。
lerp 实现 TextStyle 动画
@Composable
fun LerpDemo(){
val scope = rememberCoroutineScope()
val textStyleStart = MaterialTheme.typography.bodyLarge //start
val textStyleStop = MaterialTheme.typography.titleLarge // stop
val fractionAnim = remember { Animatable(0f) } // fraction
val animTextStyle = lerp(textStyleStart,textStyleStop,fractionAnim.value)
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Animation", style = animTextStyle)
Button(onClick = {
val targetValue = if (fractionAnim.value > 0) 0f else 1f
scope.launch {
fractionAnim.animateTo(targetValue, spring(0.4f))
}
}) {
Text(text = "Switch")
}
}
}
animateTextStyleAsState
上面用 lerp 实现了 TextStyle 动画,那么我们模仿 animateValueAsState 封装一个 animateTextStyleAsState ,以后就可以像 animate*AsState 一样调用了,岂不美滋滋。
@Composable
fun TextStyleDemo(){
var switch by remember { mutableStateOf(false) }
val textStyleStart = MaterialTheme.typography.bodyLarge //start
val textStyleStop = MaterialTheme.typography.titleLarge // stop
val targetValue = if (switch) textStyleStop else textStyleStart
val animTextStyle by animateTextStyleAsState(targetValue = targetValue,
animationSpec = tween(500, easing = BounceInterpolator().toEasing())
)
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Animation", style = animTextStyle)
Button(onClick = { switch = !switch}) {
Text(text = "Switch")
}
}
}
@Composable
fun animateTextStyleAsState(
targetValue: TextStyle,
animationSpec: AnimationSpec<Float> = remember { spring() },
finishedListener: ((TextStyle) -> Unit)? = null
): State<TextStyle> {
val listener by rememberUpdatedState(finishedListener)
val animSpec by rememberUpdatedState(animationSpec)
var startTextStyle by remember { mutableStateOf(targetValue) }
val fractionAnim = remember { Animatable(0f) }
val textStyleState = remember(fractionAnim.value){
derivedStateOf {
if (fractionAnim.value == 0f){
startTextStyle
}else{
lerp(startTextStyle,targetValue,fractionAnim.value)
}
}
}
LaunchedEffect(targetValue) {
if (targetValue != textStyleState.value){
startTextStyle = textStyleState.value
fractionAnim.snapTo(0f)Untitled.gif
fractionAnim.animateTo(1f, animSpec)
listener?.invoke(textStyleState.value)
}
}
return textStyleState
}
注意 SpringSpec 应用在 Color 类型上可能会引起值越界。