Compose中的动画

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情

我们在传统的Android开发中编写UI动画需要在drawable文件中定义一大堆动画xml文件,在Acitivity设置动画属性并开启,但是在Compose中定义动画就没有这么复杂,接下来就介绍一下Compose中的各种动画Api

Animatable

Animatable是一个基于携程的API,用于设置单个值的动画。我们可以使用此API设置颜色或浮点值的动画。它不同于所有其他动画API,因为您可以在可组合函数之外使用此API。

  1. @Composable
  2. private fun AnimatableSample() {
  3. var isAnimated by remember { mutableStateOf(false) }
  4. val color = remember { Animatable(Color.DarkGray) }
  5. LaunchedEffect(isAnimated) {
  6. color.animateTo(if (isAnimated) Color.Green else Color.Red, animationSpec = tween(2000))
  7. }
  8. Box(
  9. Modifier
  10. .fillMaxWidth()
  11. .fillMaxHeight(0.8f)
  12. .background(color.value)
  13. )
  14. Button(
  15. onClick = { isAnimated = !isAnimated },
  16. modifier = Modifier.padding(top = 10.dp)
  17. ) {
  18. Text(text = "Animate Color")
  19. }
  20. }

在之前我们不是学习过Compose的函数副作用吗,在 LaunchedEffect中,我们给出了一个基于 isAnimated值更改颜色的条件。当您按下按钮时,LaunchedEffect中的block就会改变color的颜色值,从红色切换到绿色,为了让大家看到动画效果我们给颜色切换加了间隔持续时间,使用到了tween(),我们还可以使用以下Api来实现自定义动画

  • tween

    1. @Stable
    2. fun <T> tween(
    3. durationMillis: Int = DefaultDurationMillis, //动画持续时间
    4. delayMillis: Int = 0, //动画延时开始时间
    5. easing: Easing = FastOutSlowInEasing //使用缓和曲线在起始值和结束值之间添加动画
    6. ): TweenSpec<T> = TweenSpec(durationMillis, delayMillis, easing)
  • keyframes

    keframes可以针对不同时间范围的动画帧添加动画效果

    1. @Composable
    2. fun KeyframesScreen(){
    3. val value by animateFloatAsState(
    4. targetValue = 1f,
    5. animationSpec = keyframes {
    6. durationMillis = 375
    7. 0.0f at 0 with LinearOutSlowInEasing // for 0-15 ms
    8. 0.2f at 15 with FastOutLinearInEasing // for 15-75 ms
    9. 0.4f at 75 // ms
    10. 0.4f at 225 // ms
    11. }
    12. )
    13. }
  • spring

    1. @Stable
    2. fun <T> spring(
    3. dampingRatio: Float = Spring.DampingRatioNoBouncy, //弹簧的阻尼,也就是弹性 阻尼比越低,弹簧越有弹性
    4. stiffness: Float = Spring.StiffnessMedium, //弹簧像结束值步进的速度
    5. visibilityThreshold: T? = null
    6. ): SpringSpec<T> =
    7. SpringSpec(dampingRatio, stiffness, visibilityThreshold)

    spring的动画效果和弹簧的感觉非常像

  • repeatable

    1. //执行一个重复性动画
    2. @Stable
    3. fun <T> repeatable(
    4. iterations: Int, //重复次数
    5. animation: DurationBasedAnimationSpec<T>, //重复动画
    6. repeatMode: RepeatMode = RepeatMode.Restart, //重复模式
    7. initialStartOffset: StartOffset = StartOffset(0) //动画的启示偏移时长
    8. ): RepeatableSpec<T> =
    9. RepeatableSpec(iterations, animation, repeatMode, initialStartOffset)
  • infiniteRepeatable

    infiniteRepeatable其实和repeatable非常相似,差别就在于一个是有重复次数,一个是无限重复的动画

  • snap

    snap主要用于需要提前结束动画

    1. @Stable
    2. fun <T> snap(
    3. delayMillis: Int = 0 //延时毫秒数
    4. ) = SnapSpec<T>(delayMillis)

animate*AsState

Animate*AsState主要用于为单个值提供动画,这些值可以是DpColorFloatIntegerOffsetRectSize

image.png

AnimateDpAsState主要用于widget的长度或者高度变化的动画添加

  1. @Composable
  2. fun animateDpAsState (
  3. targetValue : Dp ,
  4. animationSpec : AnimationSpec < Dp > = dpDefaultSpring ,
  5. finishedListener : ( ( Dp ) - > Unit ) ? = null
  6. )

同样也可以为其提供动画效果animationSpec

  1. fun SearchBoxForState(
  2. focusState: MutableState<Boolean>,
  3. modifier: Modifier,
  4. onSearchEvent: () -> Unit,
  5. onClick: () -> Unit
  6. ) {
  7. val searchBoxWidth by animateDpAsState(targetValue = if (focusState.value) 679.wdp else 340.wdp, animationSpec = tween())
  8. SearchBox(
  9. searchBoxWidth = searchBoxWidth,
  10. isShowClean = focusState,
  11. modifier = modifier,
  12. onSearchEvent = onSearchEvent,
  13. onClick = onClick
  14. )
  15. }

AnimateColorAsState 为颜色的变化添加过渡动画

  1. @Composable
  2. fun HomeScreen(){
  3. val bgColor by animateColorAsState(if (index % 2 == 0) whiteFFFFFFFF else grayF9F9F9)
  4. ConstraintLayout(
  5. modifier = modifier
  6. .fillMaxWidth()
  7. .height(71.hdp)
  8. .background(bgColor)
  9. ){
  10. .....
  11. }
  12. }

AnimateFloatAsState animateDpAsState() 相同。但唯一的区别是我们将浮点值作为目标值。

例如为颜色透明度添加过渡动画等等

  1. @Composable
  2. fun HomeScreen(){
  3. val alpha by animateFloatAsState(targetValue = if (isSelect) 1.0f else 0.7f)
  4. Text(
  5. text = tabName,
  6. style = TextStyle(
  7. color = whiteFFFFFFFF.copy(alpha),
  8. fontSize = 19.spi,
  9. fontWeight = if (isSelect) FontWeight.Bold else FontWeight.Medium
  10. )
  11. )
  12. }

updateTransition 同时制作多个动画

  1. @Composable
  2. fun AnimateScreen(){
  3. //动画启停的状态
  4. var isAnimate by remember { mutableStateOf(false) }
  5. //updateTransitionde 定义
  6. val transition = updateTransition(targetState = isAnimate, label = "transition")
  7. //定义关于Box大小变化的过渡动画
  8. val animateSize by transition.animateDp(transitionSpec ={
  9. tween(1000)
  10. }, label = ""){
  11. if(it) 200.wdp else 50.wdp
  12. }
  13. // //定义关于Box旋转角度变化的过渡动画
  14. val animateRotate by transition.animateFloat(transitionSpec ={
  15. tween(1000)
  16. }, label = ""){
  17. if(it) 360f else 0f
  18. }
  19. Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
  20. Column() {
  21. Box(modifier = Modifier
  22. .rotate(animateRotate)
  23. .size(animateSize)
  24. .background(green1ACFB7)
  25. )
  26. TextButton(onClick = {
  27. isAnimate = !isAnimate
  28. }) {
  29. Text(text = "开始")
  30. }
  31. }
  32. }
  33. }

InfiniteTransition

上面的动画效果都是执行一次就不会运行了,那我们怎么创建一个Box无限旋转的动画呢,接下来学习无限过渡的Api——-InfiniteTransition

通过InfiniteTransition创建的动画在进入可组合项时就会开始运行,直到他们从和组合项中移除才会停止

  1. @Composable
  2. fun InfiniteTransitionScreen(){
  3. val infiniteTransition = rememberInfiniteTransition()
  4. val infiniteRotate by infiniteTransition.animateFloat(
  5. initialValue = 0f,
  6. targetValue = 360f,
  7. animationSpec = infiniteRepeatable(
  8. animation = tween(200),
  9. repeatMode = RepeatMode.Restart //重复模式Restart是重启,Reverses是反转
  10. )
  11. )
  12. Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
  13. Column() {
  14. Box(modifier = Modifier
  15. .rotate(infiniteRotate)
  16. .size(100.wdp)
  17. .background(green1ACFB7)
  18. )
  19. }
  20. }
  21. }

我们不仅能使用infiniteTransition.animateFloat创建子动画,还可以使用InfiniteTransition.animateColorInfiniteTransition.animateValue这些方式来创建一个无限运行的子动画

更多的详情可以参考官方文档


文章标签:

原文连接:https://juejin.cn/post/7109358158161641480

相关推荐

使用 PixCap 和 ReadyPlayerMe 快速制作3D 模型动画

七夕快到了,用SwiftUI做一个表达爱意的心形动画

超酷炫的转场动画?CSS 轻松拿下!

【动画实现】CSS Houdini 实现磁吸 🧲 效果

「万字总结」看不懂源码不要慌,🍒动画 + 大白话讲清楚React渲染原理

练习动画最好的方式:封面过渡

iOS怎么用代码实现这样奇怪的动画

语音驱动嘴型与面部动画生成的现状和趋势

兼职PIXI时入3K?动画特效就是这么给力!(全注释,附源码)| 猿创营

Tweenjs动画实例~

动画合成小技巧!CSS 实现动感的倒计时效果

CSS Houdini:用浏览器引擎实现高级CSS效果

前端页面如何使用pixi.js与gsap.js实现单车刹车的动画效果 | 猿创营

【初始C语言】/*C语言初阶指针通俗详解*/

Vue.js\u002FNuxt.js在页面加载完成前显示Loading动画

你不能只会flex居中布局,精制动画讲解所有flex布局方式!通俗易懂纯干货教程!

Flutter 贝塞尔曲线动画的实现思路(水滴分页指示器)

搞懂动画原理,学会动画函数requestAnimationFrame的使用和特点!

LVGL库入门教程 - 动画

计算机艺术和动画之父肯·诺尔顿去世,享年91岁