Jetpack Compose自定义动画与Animatable详解
作者:唯鹿 发布时间:2021-07-04 20:26:08
本篇主要是自定义动画与Animatable
。
AnimationSpec
上一篇中,出现了多次animationSpec
属性,它是用来自定义动画规范的。例如:
fun Modifier.animateContentSize(
animationSpec: FiniteAnimationSpec<IntSize> = spring(),
finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
): Modifier
下面是几种自带的动画规范:
1.spring
我们上一篇也简单说到了,它是一种弹性动画。源码如下:
@Stable
fun <T> spring(
dampingRatio: Float = Spring.DampingRatioNoBouncy,
stiffness: Float = Spring.StiffnessMedium,
visibilityThreshold: T? = null
): SpringSpec<T> = SpringSpec(dampingRatio, stiffness, visibilityThreshold)
dampingRatio
: 定义弹簧的弹性,数值越小弹性越高。默认值DampingRatioNoBouncy
为1f。下图为其他数值的效果:
stiffness
: 定义弹簧向结束值移动的速度。默认值StiffnessMedium
为1500f。visibilityThreshold
:可见临界值(动画停止的值)。比如目标150dp,此值设置10dp,那么回弹距离小于这个值时就会停止弹性效果。
2.tween
在指定的时间内使用缓和曲线在起始值和结束值之间添加动画效果。
@Stable
fun <T> tween(
durationMillis: Int = DefaultDurationMillis,
delayMillis: Int = 0,
easing: Easing = FastOutSlowInEasing
): TweenSpec<T> = TweenSpec(durationMillis, delayMillis, easing)
val FastOutSlowInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f)
durationMillis
:动画的持续时间,默认值300毫秒。delayMillis
:延迟动画开始的时间。easing
:两个值之间的插值曲线,类似Android中的Interpolator
插值器。目前compose中提供了五种类型,这里就不一一说明了,实际使用时可以尝试一下。当然你也可以使用CubicBezierEasing
甚至Easing
自定义曲线。
3.keyframes
在动画时长内的不同时间戳中指定快照值,实现动画效果。在任何给定时间,动画值都将插值到两个关键帧值之间。对于其中每个关键帧,您都可以指定 Easing
来确定插值曲线。
@Stable
fun <T> keyframes(
init: KeyframesSpec.KeyframesSpecConfig<T>.() -> Unit
): KeyframesSpec<T> {
return KeyframesSpec(KeyframesSpec.KeyframesSpecConfig<T>().apply(init))
}
例子:
val value by animateFloatAsState(
targetValue = 1f,
animationSpec = keyframes {
durationMillis = 375
0.0f at 0 with LinearOutSlowInEasing // for 0-15 ms
0.2f at 15 with FastOutLinearInEasing // for 15-75 ms
0.4f at 75 // ms
0.4f at 225 // ms
}
)
解释一下上面的例子:整体起始值0和结束值1,动画时长375毫秒。
从0开始到0.2结束,前15毫秒使用
LinearOutSlowInEasing
过渡。从0.2开始到0.4结束,15到75毫秒使用
FastOutLinearInEasing
过渡。从0.4开始到0.4结束,75到225毫秒匀速过渡。(静止效果)
从0.4开始到1结束,225毫秒到结束匀速过渡。
效果图如下:
keyframes
同样也可以设置delayMillis
指定延时时间。
4.repeatable
重复运行基于时长的动画,直至达到指定的迭代计数。
@Stable
fun <T> repeatable(
iterations: Int,
animation: DurationBasedAnimationSpec<T>,
repeatMode: RepeatMode = RepeatMode.Restart,
initialStartOffset: StartOffset = StartOffset(0)
): RepeatableSpec<T> = RepeatableSpec(iterations, animation, repeatMode, initialStartOffset)
iterations
,动画运行的次数。1表示不重复。建议使用infiniteRepeatable创建无限重复的动画。animation
,设置需要重复的动画。repeatMode
,重复模式。指定动画是从头开始 (RepeatMode.Restart) 还是从结尾开始 (RepeatMode.Reverse) 重复播放。initialStartOffset
,可用于延迟动画的开始或快进动画到给定的播放时间。如果设置StartOffset(1500)
就是延迟动画。 设置StartOffset(1500, offsetType = StartOffsetType.FastForward)
就是快进动画。默认情况下,延时时长为0。
如果需要动画重复无限次,可以使用infiniteRepeatable
,用法与repeatable
一致。
5.snap
snap
是特殊的 AnimationSpec
,它会立即将值切换到结束值。您可以指定 delayMillis 来延迟动画播放的开始时间。
@Stable
fun <T> snap(delayMillis: Int = 0) = SnapSpec<T>(delayMillis)
Animatable
Animatable
是一个值容器,它可以在通过 animateTo
更改值时为值添加动画效果。它可确保一致的连续性和互斥性,这意味着值变化始终是连续的,并且会取消任何正在播放的动画。
Animatable
的许多功能(包括 animateTo)以挂起函数的形式提供。这意味着,它们需要封装在适当的协程作用域内。例如,您可以使用 LaunchedEffect
可组合项针对指定键值的时长创建一个作用域。
// Start out gray and animate to green/red based on `ok`
val color = remember { Animatable(Color.Gray) }
LaunchedEffect(ok) {
color.animateTo(if (ok) Color.Green else Color.Red)
}
Box(Modifier.fillMaxSize().background(color.value))
在上面的示例中,我们创建并记住了初始值为 Color.Gray
的 Animatable
实例。根据布尔标记 ok
的值,颜色将以动画形式呈现 Color.Green
或 Color.Red
。对该布尔值的任何后续更改都会使动画开始使用另一种颜色。如果更改该值时有正在播放的动画,系统会取消该动画,并且新动画将以当前速度从当前快照值开始播放。
与 animate*AsState
相比,使用 Animatable
可以直接对以下几个方面进行更精细的控制。首先,Animatable
的初始值可以与第一个目标值不同。例如,上面的代码示例首先显示一个灰色框,然后立即开始通过动画呈现为绿色或红色。其次,Animatable
对内容值提供更多操作(即 snapTo
和 animateDecay
)。snapTo
可立即将当前值设为目标值。如果动画本身不是唯一的可信来源,且必须与其他状态(如触摸事件)同步,该函数就非常有用。animateDecay
用于启动播放从给定速度变慢的动画(衰减)。这有助于实现投掷行为。
下面看一个官方demo:
Column(modifier = Modifier.fillMaxWidth()) {
// Creates an `Animatable` to animate Offset and `remember` it.
val animatedOffset = remember { Animatable(Offset(0f, 0f), Offset.VectorConverter) }
Box(
Modifier.fillMaxSize().background(Color(0xffb99aff)).pointerInput(Unit) {
coroutineScope {
while (true) {
val offset = awaitPointerEventScope {
awaitFirstDown().position
}
// Launch a new coroutine for animation so the touch detection thread is not
// blocked.
launch {
// Animates to the pressed position, with the given animation spec.
animatedOffset.animateTo(
offset,
animationSpec = spring(stiffness = Spring.StiffnessLow)
)
}
}
}
}
) {
Text("Tap anywhere", Modifier.align(Alignment.Center))
Box(
Modifier
.offset {
// Use the animated offset as the offset of the Box.
IntOffset(
animatedOffset.value.x.roundToInt(),
animatedOffset.value.y.roundToInt()
)
}
.size(40.dp)
.background(Color(0xff3c1361), CircleShape)
)
}
}
效果图这里我就不放了。就是页面上有个40*40dp的圆形view,点击屏幕任意位置,他就会移动过去。这里核心靠的就是animateTo
方法,而且无论我们中间如果快速点击不同位置,系统会取消上一个动画,接着当前位置移动到目标位置。
从上面这张图可以看出各个API之间的关系。所有这些 API 都基于更基础的 Animation
API。虽然大多数应用不会直接使用 Animation
,但 Animation
的某些自定义功能可以通过更高级别的 API 获得。
到此,Compose的动画部分大体结束。
参考
Compose官方文档
Compose官方文档 – Animatable
来源:https://weilu.blog.csdn.net/article/details/125997035


猜你喜欢
- 这两个update都是使用generator生成的mapper.xml文件中,对dao层的更新操作update更新传回数据的所有字段,没有传
- 本文主要给大家分享如何在全局上去监听 click 点击事件,并做些通用处理或是拦截。使用场景可能就是具体的全局防快速重复点击,或是通用打点分
- 什么是冒泡排序冒泡排序指重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从小到大)错误就把他们交换过来。走访元素的工作是重复
- 本文实例讲述了Android实现彩信附件的添加与删除功能。分享给大家供大家参考,具体如下:添加附件在ComposeMessageActivi
- smoothstep另一种用法在之前OpenGL Shader-抗锯齿实现文章中所介绍的那样:为了抗锯齿效果可以用smoothstep函数对
- 本文介绍了Flutter 实现下拉刷新上拉加载的示例代码,分享给大家,具体如下:效果图 使用方法添加依赖depende
- 这里不准备讨论REST的细节内容,但是总体上,REST是让客户端与服务器段的交互通过发送和接收展示资源的方式来进行,在这里有必要说明:Fie
- 1. mybatis-plus开启二级缓存spring: datasource: type: com.alibaba.druid
- 前言:前面总结学习了图片的使用以及Lru算法,今天来学习一下比较优秀的图片缓存开源框架。技术本身就要不断的更迭,从最初的自己使用SoftRe
- MyBatis根据条件批量修改字段背景:给学生改作业,只要是对的都批量进行数据库的修改代码以及注释conttoller@RestContro
- 目录1 Exchanger 介绍2 Exchanger 实例exchange等待超时3 实现原理1 Exchanger 介绍前面分别介绍了C
- 一、概述运行时变更就是设备在运行时发生变化(例如屏幕旋转、键盘可用性及语言)。发生这些变化,Android会重启Activity,这时就需要
- 本文实例讲述了C#实现输入10个数存入到数组中并求max和min及平均数的方法。分享给大家供大家参考,具体如下:using System;u
- 函数名称 说明ActiveKeyboardLayout 激活一个不同的键盘布局,该布局必须先由 LoadKeyBoardLayout函数装载
- 这篇文章主要介绍了JDK线程池和Spring线程池的使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 引言二分查找底层依赖的是数组随机访问的特性,所以只能用数组来实现。如果数据存储在链表中,就真的没法用二分查找算法了吗?实际上,只需要对链表稍
- 开门见山,添加水印的方法非常简单,其实就只有3个步骤:1、载入原始图片2、载入水印图片3、保存带有水印的图片实现的原理就是:获取原始图片的宽
- 介绍备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获并保存一个对象的内部状态,并在之后可
- 微信支付流程都是我自己工作中开发的,亲测可用,不喜勿喷。controller中我是这么写的,你们需要根据自己的业务需求改动。Response
- 一、为什么要控制当你在项目启动时需要提前做一个业务的初始化工作时,或者你正在开发某个中间件需要完成自动装配时。你会声明自己的Configur