1. AnimationSpec是什么
Android Compose
中的
AnimationSpec
用来自定义动画规格。
啥意思呢,其实就是类似于
传统View体系
中的差值器
Interpolator
,但是比起差值器,又提供了更多的功能。
根据其不同的子类,可以来控制动画执行时长、设置动画执行多少时间到动画的某个位置、实现仿弹簧的动画效果、指定动画执行次数等等功能。
val alpha: Float by animateFloatAsState(
targetValue = if (enabled) 1f else 0.5f,
animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing) //这里的tween就是个AnimationSpec
)
先来看下
Compose
中所有的
AnimationSpec
看上去很多是不是,但实际上我们只需要记住这几个重点的就行
其中,蓝色背景的是可以直接进行使用的 (实现类),灰色背景的是接口,
AnimationSpec
也是一个接口。
interface AnimationSpec<T> {
fun <V : AnimationVector> vectorize(
converter: TwoWayConverter<T, V>
): VectorizedAnimationSpec<V>
}
1. SpringSpec
SpringSpec
实现了
AnimationSpec
接口,提供了一些列基于弹簧的物理动画效果。
很多动画内部
AnimationSpec
使用的默认值都是
spring
,比如
animateXXXAsState
以及
updateTransition
等。
因为
spring
的动画效果基于物理原理,使动画更真实自然。
同时,因为
spring
是基于物理规律的,所以无法指定动画执行时长,而是会基于物理规律来确定动画执行时长。
class SpringSpec<T>(
val dampingRatio: Float = Spring.DampingRatioNoBouncy,
val stiffness: Float = Spring.StiffnessMedium,
val visibilityThreshold: T? = null
) : FiniteAnimationSpec<T>
spring
有三个传参,
dampingRatio
、
stiffness
和
visibilityThreshold
,前两个参数主要用来控制弹跳动画的动画效果。
1.1 使用示例
var change by remember {
mutableStateOf(false)
}
val offsetX = remember(change) {
if (change) 350.dp else 50.dp
}
val offsetXAnim = remember {
Animatable(offsetX, Dp.VectorConverter)
}
LaunchedEffect(key1 = change, block = {
offsetXAnim.animateTo(
offsetX, spring(Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessMedium)
)
})
Box(modifier = Modifier
.padding(offsetXAnim.value, 0.dp, 0.dp, 0.dp)
.size(100.dp)
.background(Color.Blue)
.clickable { change = !change })
效果如下所示
1.2 dampingRatio
dampingRation
表示弹簧的阻尼比。这个值越大,阻尼越大。
不同的
dampingRatio
有不同的效果,如下图所示
如果不额外指定,默认会使用
DampingRationNoBouncy
,是不带弹簧效果的。
需要注意dampingRation不能小于0
1.3 stiffness
stiffness
定义弹簧的刚度值,弹簧有多想变回去,这个值越大,回弹的越快。
Compose
为
stiffness
定义的常量如下,默认值是
StiffnessMedium
const val StiffnessHigh = 10_000f
const val StiffnessMedium = 1500f
const val StiffnessMediumLow = 400f
const val StiffnessLow = 200f
const val StiffnessVeryLow = 50f
需要注意stiffness必须大于0
1.4 visibilityThreshold
visibilityThreshold
用来指定一个阈值,当动画到达这个阈值时,动画会立即停止。
2. TweenSpec
这里的
Tween
不是老的
View体系
动画中的补间动画,而是和
Interpolator
(差值器)是一个作用的东西。
tween
动画必须在规定时间内完成,它不能像
SpringSpec
那样完全基于物理规律进行动画,
class TweenSpec<T>(
val durationMillis: Int = DefaultDurationMillis,
val delay: Int = 0,
val easing: Easing = FastOutSlowInEasing
)
tween
有3个参数
-
durationMillis
: 动画执行时间 (
ms
) -
delayMillis
: 延迟多久开始执行 -
Easing
: 衰减曲线动画效果,用来配置怎么进行渐变的
2.1 使用示例
var change by remember {
mutableStateOf(false)
}
val offsetX = remember(change) {
if (change) 300.dp else 0.dp
}
val offsetXAnim = remember {
Animatable(offsetX, Dp.VectorConverter)
}
LaunchedEffect(key1 = change, block = {
//offsetXAnim.animateTo(offsetX, TweenSpec(easing = LinearEasing))
//tween是TweenSpec的简便写法,这两种都是可以用的
offsetXAnim.animateTo(
offsetX, tween(durationMillis = 1000, delayMillis = 0, easing = FastOutSlowInEasing)
)
})
Box(modifier = Modifier
.padding(offsetXAnim.value, 0.dp, 0.dp, 0.dp)
.size(100.dp)
.background(Color.Blue)
.clickable { change = !change })
效果如下所示
2.2 Easing
Compose
中预置的
Easing
有这些
//先加速后减速(默认),相当于差值器中的FastOutSlowInInterpolator
//和AccelerateDecelerateInterpolator类似 (区别和FastOutSlowInInterpolator在于曲线幅度稍有不同,下面几个同理)
val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
//入场速度快,慢慢减速到0,相当于差值器中的LinearOutSlowInInterpolator,和DecelerateInterpolator类似
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
//从0慢慢加速到很快,相当于差值器中的FastOutLinearInInterpolator,和AccelerateInterpolator类似
val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
//线性
val LinearEasing: Easing = Easing { fraction -> fraction }
2.2.1 示意曲线
2.2.2 自定义Easing
使用
CubicBezierEasing
可以自定义
Easing
,
使用贝塞尔曲线
API
网站 :
cubic-bezier.com
,专门用来展现三阶贝塞尔曲线的效果的,可以查到对应三阶贝塞尔曲线具体的参数,我们传入到
CubicBezierEasing
中就可以自定义
Easing
了。
比如这里
-
FastOutSlowInEasing
对应的参数是
CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
-
LinearOutSlowInEasing
对应的参数是
CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
3. SnapSpec
即时生效动画规格,和
anim.snapTo
的效果是一样的
anim.snapTo()
anim.animateTo(offset,SnapSpec(delayMillis=100/*可以设置延迟*/))
anim.animateTo(offset,snap(delayMillis=100/*可以设置延迟*/))
3.1 使用示例
var change by remember {
mutableStateOf(false)
}
val offsetX = remember(change) {
if (change) 300.dp else 0.dp
}
val offsetXAnim = remember {
Animatable(offsetX, Dp.VectorConverter)
}
LaunchedEffect(key1 = change, block = {
offsetXAnim.animateTo(
offsetX, snap(delayMillis = 100)
)
})
Box(modifier = Modifier
.padding(offsetXAnim.value, 0.dp, 0.dp, 0.dp)
.size(100.dp)
.background(Color.Blue)
.clickable { change = !change })
效果如下所示
4. KeyframesSpec
关键帧动画规格,相当于可以分段的
tweenSpec
,但是动画的复用性会降低
anim.animateTo(size,keyframes{
//infix function 中缀函数
250.dp at 0 with LinearEasing //作用于 0毫秒-150毫秒
500.dp at 150 with FastOutSlowInEasing//作用于150毫秒及之后 //默认是LinearEasing
durationMillis = 450 //执行时长
delayMillis = 50 //延迟多久执行
})
1.until(100) —-> 1 until 100 // 这也是一个中缀函数
4.1 使用示例
var change by remember {
mutableStateOf(false)
}
val offsetX = remember(change) {
if (change) 300.dp else 0.dp
}
val offsetXAnim = remember {
Animatable(offsetX, Dp.VectorConverter)
}
LaunchedEffect(key1 = change, block = {
offsetXAnim.animateTo(offsetX, keyframes {
80.dp at 250 with FastOutSlowInEasing //0至150毫秒
durationMillis = 1000 //动画持续多久
delayMillis = 50 //延迟多久执行
})
})
Box(modifier = Modifier
.padding(offsetXAnim.value, 0.dp, 0.dp, 0.dp)
.size(100.dp)
.background(Color.Blue)
.clickable { change = !change })
显示效果
5. RepeatableSpec
循环动画规格,
SpringSpec
不能放进
RepeatableSpec
里面,必须是
DurationBasedAnimationSpec
的子类,
SpringSpec
属于物理弹簧效果,真实环境下肯定是无法持续循环的。
fun <T> repeatable(
iterations: Int, //重复次数
animation: DurationBasedAnimationSpec<T>,
repeatMode: RepeatMode = RepeatMode.Restart, //重复模式 : 重新开始 / 翻转
initialStartOffset: StartOffset = StartOffset(0) //初始偏移(时间偏移)
)
5.1 使用示例
var change by remember {
mutableStateOf(false)
}
val offsetX = remember(change) {
if (change) 300.dp else 0.dp
}
val offsetXAnim = remember {
Animatable(offsetX, Dp.VectorConverter)
}
LaunchedEffect(key1 = change, block = {
RepeatMode.Restart
offsetXAnim.animateTo(offsetX, repeatable(3,tween(),RepeatMode.Reverse))
})
Box(modifier = Modifier
.padding(offsetXAnim.value, 0.dp, 0.dp, 0.dp)
.size(100.dp)
.background(Color.Blue)
.clickable { change = !change })
效果如下所示
6. InfiniteRepeatableSpec
InfiniteRepeatableSpec
是无限动画规格,会无限执行。
和
RepeatableSpec
本质上没有区别,只有一个区别,无限和有限。
anim.animateTo(size, infiniteRepeatable(tween(),RepeatMode.Reverse))
效果如下所示
7. Compose 动画系列
Compose 动画系列
Compose 动画 (一) : animateXxxAsState 实现放大/缩小/渐变等效果
Compose 动画 (二) : 为什么animateDpAsState要用val ? MutableState和State有什么区别 ?
Compose 动画 (三) : AnimatedVisibility 从入门到深入
Compose 动画 (四) : AnimatedVisibility 各种入场和出场动画效果
Compose 动画 (五) : animateContentSize / animateEnterExit / Crossfade / AnimatedContent
Compose 动画 (六) : 使用Transition管理多个动画,实现动画预览
Compose 动画 (七) : 高可定制性的动画 Animatable
Compose 动画 (八) : Compose中的动画差值器 AnimationSpec
本文为氦客在CSDN上独家发布
地址 :
https://blog.csdn.net/EthanCo/article/details/129882487