在上一篇文章Compose开箱即用的动画API中,在compose动画的学习运用过程中,有一个参数特点一向存在,那便是AnimationSpec。不管是用于为单个值增加动画作用的animate*AsState,仍是用于为多个值增加动画作用的updateTransition,亦或是一些封装好的高档别动画API:animatedContentSize、AnimatedVisibility,在这些里面都存在着AnimationSpec这个参数特点。但假如你并不了解并不会运用AnimationSpec,也不会阻止你运用这些动画API,由于这些动画API都供给了默认的AnimationSpec完成。
interface AnimationSpec<T> {
fun <V : AnimationVector> vectorize(
converter: TwoWayConverter<T, V>
): VectorizedAnimationSpec<V>
}
AnimationSpec,the specification of an animation,是一个接口,它用来存储动画规范,包含要进行动画处理的数据类型、将数据转换为动画后将运用的动画装备。 Compose现已为咱们完成了一些常用的AnimationSpec。 官方供给了8种能够直接运用的(蓝色标示的为类,能够直接运用)。
SpringSpec
SpringSpec,弹性动画,是许多动画的默认AnimationSpec完成,例如animate*AsState、updateTransition、animatedContentSize等这些动画。
@Stable
fun <T> spring(
dampingRatio: Float = Spring.DampingRatioNoBouncy,
stiffness: Float = Spring.StiffnessMedium,
visibilityThreshold: T? = null
): SpringSpec<T> =
SpringSpec(dampingRatio, stiffness, visibilityThreshold)
官方供给了spring()办法用于结构SpringSpec,接收三个参数,但都有其默认值。
- dampingRatio:阻尼比,默认值为Spring.DampingRatioNoBouncy = 1f,也便是没有弹性。当阻尼比<1时,阻尼比越低,绷簧越有弹性。
- stiffness:刚度,默认值为Spring.StiffnessMedium。
- visibilityThreshold:可见性阈值。
dampingRatio和stiffness都各有5个枚举值。
@Composable
fun SpringDemo() {
var small by remember {
mutableStateOf(true)
}
val size: Dp by animateDpAsState(
targetValue = if (small) 40.dp else 100.dp,
animationSpec = spring(
dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessHigh
)
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { small = !small }) {
Text(text = "改动方块巨细-spring")
}
Box(
modifier = Modifier
.size(size)
.background(Color.LightGray)
)
}
}
咱们运用一个size巨细动画来调查SpringSpec作用,修正上述代码的spring中参数能够调查到各种SpringSpec作用,在相同的dampingRatio = Spring.DampingRatioNoBouncy,也便是没有弹性时,刚度越小,动画时刻越长,动画作用越明显。而在较低的刚度下,阻尼比越小,弹性动画作用就越明显。
TweenSpec
TweenSpec,用来创建运用给定的持续时刻、推迟时刻和缓和曲线装备的动画规范。
@Stable
fun <T> tween(
durationMillis: Int = DefaultDurationMillis,
delayMillis: Int = 0,
easing: Easing = FastOutSlowInEasing
): TweenSpec<T> = TweenSpec(durationMillis, delayMillis, easing)
官方供给了tween()办法用于结构TweenSpec,接收三个参数,但都有其默认值。
- durationMillis:动画持续时刻,默认值为300毫秒。
- delayMillis:动画推迟时刻,默认值为0,也便是当即开端动画。
- easing:动画曲线改动,默认值为FastOutSlowInEasing
@Stable
fun interface Easing {
fun transform(fraction: Float): Float
}
Easing是一个接口,是一种调整动画分数的办法,答应过渡元素加快或减速,而不是以恒定的速度移动,Easing中有一个办法,办法中参数 fraction 是一个在0到1.0之间的值,表明动画中的当前进展点,其间0表明开端,1.0表明完毕。
Easing只要一个完成类CubicBezierEasing,CubicBezierEasing完成了三阶贝塞尔曲线。官方引荐与其创建新实例,不如考虑运用一种常见的 cubic Easings。 官方供给4种常用的Easing完成:
//以停止开端和完毕的元素运用此规范缓动。 他们快速加快并逐渐减速,以着重过渡的完毕。这是最常见的办法。这相当于原生安卓插值器FastOutSlowInInterpolator
val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
//传入的元素运用减速缓动进行动画处理,它以峰值速度(元素运动的最快点)开端过渡并在停止时完毕。这相当于原生安卓插值器LinearOutSlowInInterpolator
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)
//退出屏幕的元素运用加快缓动,它们从停止开端并以峰值速度完毕。这相当于原生安卓插值器FastOutLinearInInterpolator
val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f)
//线性、匀速缓动
val LinearEasing: Easing = Easing { fraction -> fraction }
咱们将SpringSpec中的案例运用spring改为运用tween来调查TweenSpec作用,修正代码的tween中参数能够调查到各种TweenSpec作用。为了能更好的调查到动画作用,能够将动画时刻恰当设置长一点。
@Composable
fun TweenDemo() {
var small by remember {
mutableStateOf(true)
}
val size: Dp by animateDpAsState(
targetValue = if (small) 40.dp else 100.dp,
animationSpec = tween(
durationMillis = 3000,
easing = FastOutSlowInEasing
)
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { small = !small }) {
Text(text = "改动方块巨细-tween")
}
Box(
modifier = Modifier
.size(size)
.background(Color.LightGray)
)
}
}
SnapSpec
SnapSpec,描绘了一种跳切类型的动画。 它当行将动画值捕捉到最终值。
@Stable
fun <T> snap(delayMillis: Int = 0) = SnapSpec<T>(delayMillis)
官方供给了snap()办法用于结构SnapSpec,只要一个参数,delayMillis:动画推迟时刻,默认值为0,也便是当即开端动画。
@Immutable
class SnapSpec<T>(val delay: Int = 0) : DurationBasedAnimationSpec<T> {
override fun <V : AnimationVector> vectorize(
converter: TwoWayConverter<T, V>
): VectorizedDurationBasedAnimationSpec<V> = VectorizedSnapSpec(delay)
}
SnapSpec完成了DurationBasedAnimationSpec接口,还有上面现已介绍过的TweenSpec也是完成了DurationBasedAnimationSpec接口,此外还有KeyframesSpec。
@Immutable
class TweenSpec<T>(
val durationMillis: Int = DefaultDurationMillis,
val delay: Int = 0,
val easing: Easing = FastOutSlowInEasing
) : DurationBasedAnimationSpec<T> {
override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>) =
VectorizedTweenSpec<V>(durationMillis, delay, easing)
}
interface DurationBasedAnimationSpec<T> : FiniteAnimationSpec<T> {
override fun <V : AnimationVector> vectorize(converter: TwoWayConverter<T, V>):
VectorizedDurationBasedAnimationSpec<V>
}
DurationBasedAnimationSpec,描绘了根据固定持续时刻的 AnimationSpecs,例如 KeyframesSpec、TweenSpec 和 SnapSpec。 这些根据持续时刻的AnimationSpec在放入RepeatableSpec时能够重复执行。
咱们将上面的案例改为运用snap来调查SnapSpec作用,将动画推迟1秒来调查snap跳切的作用。
@Composable
fun SnapDemo() {
var small by remember {
mutableStateOf(true)
}
val size: Dp by animateDpAsState(
targetValue = if (small) 40.dp else 100.dp,
animationSpec = snap(
delayMillis = 1000
)
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { small = !small }) {
Text(text = "改动方块巨细-snap")
}
Box(
modifier = Modifier
.size(size)
.background(Color.LightGray)
)
}
}
KeyframesSpec
KeyframesSpec,根据动画持续时刻中不同时刻戳界说的值(即不同的关键帧)来制造动画。每个关键帧都能够运用KeyframesSpecConfig.at来进行界说。
@Stable
fun <T> keyframes(
init: KeyframesSpec.KeyframesSpecConfig<T>.() -> Unit
): KeyframesSpec<T> {
return KeyframesSpec(KeyframesSpec.KeyframesSpecConfig<T>().apply(init))
}
官方供给了keyframes()办法用于结构KeyframesSpec,只要一个参数KeyframesSpec.KeyframesSpecConfig。
class KeyframesSpecConfig<T> {
//动画持续时刻,默以为300
var durationMillis: Int = DefaultDurationMillis
//动画推迟时刻,默以为0
var delayMillis: Int = 0
//关键帧
internal val keyframes = mutableMapOf<Int, KeyframeEntity<T>>()
//增加一个关键帧在某个时刻点时刻
infix fun T.at(/*@IntRange(from = 0)*/ timeStamp: Int): KeyframeEntity<T> {
return KeyframeEntity(this).also {
keyframes[timeStamp] = it
}
}
//增加一个关键帧在某个进展时刻
infix fun T.atFraction(fraction: Float): KeyframeEntity<T> {
return at((durationMillis * fraction).roundToInt())
}
//为刚供给的时刻戳开端的时刻间隔增加 Easing
infix fun KeyframeEntity<T>.with(easing: Easing) {
this.easing = easing
}
KeyframesSpecConfig存储关键帧的可变装备,包含 durationMillis、delayMillis 和所有关键帧。 每个关键帧都界说了特定时刻的动画值。
看一下具体运用和作用:
@Composable
fun KeyframesDemo() {
var small by remember {
mutableStateOf(true)
}
val size: Dp by animateDpAsState(
targetValue = if (small) 40.dp else 100.dp,
animationSpec = keyframes {
durationMillis = 1000
50.dp at 0 with LinearOutSlowInEasing
60.dp at 100 with FastOutLinearInEasing
70.dp at 300
80.dp at 600
}
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { small = !small }) {
Text(text = "改动方块巨细-keyframes")
}
Box(
modifier = Modifier
.size(size)
.background(Color.LightGray)
)
}
}
RepeatableSpec
RepeatableSpec,构建一个根据DurationBasedAnimationSpec的重复动画。
@Stable
fun <T> repeatable(
iterations: Int,
animation: DurationBasedAnimationSpec<T>,
repeatMode: RepeatMode = RepeatMode.Restart,
initialStartOffset: StartOffset = StartOffset(0)
): RepeatableSpec<T> =
RepeatableSpec(iterations, animation, repeatMode, initialStartOffset)
官方供给了repeatable()办法用于结构RepeatableSpec,四个参数,需求至少传入两个参数。
- iterations:重复次数,理论上应大于1,等于1表明不重复,也就没有必要运用RepeatableSpec。
- animation:将被重复的AnimationSpec,有必要是DurationBasedAnimationSpec,也便是能够运用KeyframesSpec 、SnapSpec 和TweenSpec。
- repeatMode:重复形式
- initialStartOffset:动画开端的偏移
RepeatMode有两种形式:
enum class RepeatMode {
//将重新启动动画,并从开端值到完毕值进行动画处理。
Restart,
//将在动画重复时回转上一次迭代
Reverse
}
在 RepeatMode.Reverse 形式下重复时,强烈建议迭代次数为奇数。否则,动画可能会在完成最终一次迭代时跳转到完毕值。
initialStartOffset 可用于推迟动画的开端或将动画快进到给定的播映时刻。 此开端偏移量不会重复,而动画中的推迟(假如有)将重复。 默认情况下,偏移量为 0。
@kotlin.jvm.JvmInline
value class StartOffset private constructor(internal val value: Long) {
constructor(offsetMillis: Int, offsetType: StartOffsetType = StartOffsetType.Delay) : this(
(offsetMillis * offsetType.value).toLong()
)
val offsetMillis: Int
get() = abs(this.value.toInt())
val offsetType: StartOffsetType
get() = when (this.value > 0) {
true -> StartOffsetType.FastForward
false -> StartOffsetType.Delay
}
}
StartOffset存储一个offsetMillis时刻和StartOffsetType类型,StartOffsetType类型有两类:StartOffsetType.Delay推迟动画的开端和StartOffsetType.FastForward快进动画到给定的播映时刻,并当即开端播映。
@kotlin.jvm.JvmInline
value class StartOffsetType private constructor(internal val value: Int) {
companion object {
//推迟动画的开端
val Delay = StartOffsetType(-1)
//快进动画到给定的播映时刻,并当即开端播映。
val FastForward = StartOffsetType(1)
}
}
运用repeatable,将animation设置为一个1秒的匀速改动的tween动画,重复3次,重复办法为回转重复,initialStartOffset设置为推迟500毫秒,StartOffsetType.FastForward。
@Composable
fun RepeatableDemo() {
var small by remember {
mutableStateOf(true)
}
val size: Dp by animateDpAsState(
targetValue = if (small) 40.dp else 100.dp,
animationSpec = repeatable(
iterations = 3,
animation = tween(durationMillis = 1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse,
initialStartOffset = StartOffset(500, StartOffsetType.FastForward)
)
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { small = !small }) {
Text(text = "改动方块巨细-repeatable")
}
Box(
modifier = Modifier
.size(size)
.background(Color.LightGray)
)
}
}
整个动画作用:快进到500毫秒处,然后当即开端匀速动画,又500毫秒后开端回转动画。
InfiniteRepeatableSpec
InfiniteRepeatableSpec,构建一个根据DurationBasedAnimationSpec的无限重复动画。
@Stable
fun <T> infiniteRepeatable(
animation: DurationBasedAnimationSpec<T>,
repeatMode: RepeatMode = RepeatMode.Restart,
initialStartOffset: StartOffset = StartOffset(0)
): InfiniteRepeatableSpec<T> =
InfiniteRepeatableSpec(animation, repeatMode, initialStartOffset)
官方供给了infiniteRepeatable()办法用于结构InfiniteRepeatableSpec,相比RepeatableSpec少了一个参数iterations,无限重复动画自然是不需求重复次数的,其余参数都一样。
@Composable
fun InfiniteRepeatableDemo() {
var small by remember {
mutableStateOf(true)
}
val size: Dp by animateDpAsState(
targetValue = if (small) 40.dp else 100.dp,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1000, easing = LinearEasing),
repeatMode = RepeatMode.Reverse,
initialStartOffset = StartOffset(500, StartOffsetType.FastForward)
)
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = { small = !small }) {
Text(text = "改动方块巨细-infiniteRepeatable")
}
Box(
modifier = Modifier
.size(size)
.background(Color.LightGray)
)
}
}
FloatAnimationSpec
FloatAnimationSpec是一个接口,有两个完成类,FloatTweenSpec仅针对Float类型做TweenSpec动画,FloatSpringSpec仅针对Float类型做SpringSpec动画。官方没有供给能够直接进行运用的办法,由于tween()和spring()支撑全量数据类型,FloatAnimationSpec是底层做更精细的计算的时候才会去运用。