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
有这些
//先加速后减速(默认),相当于差值器中的AccelerateDecelerateInterpolator
val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
//入场速度快,慢慢减速到0,相当于差值器中的DecelerateInterpolator
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
//从0慢慢加速到很快,相当于差值器中的AccelerateInterpolator
val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
//线性
val LinearEasing: Easing = Easing { fraction -> fraction }
2.1.1 LinearEasing
LaunchedEffect(key1 = big, block = {
anim.animateTo(size, TweenSpec(easing = LinearEasing))
})
2.3 自定义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))
效果如下所示