Compose 动画艺术探索之属性动画

news/2024/5/20 2:48:10/文章来源:https://blog.csdn.net/haojiagou/article/details/128158349

本篇文章是此专栏的第三篇文章,如果想阅读前两篇文章的话请点击下方链接:

  • Compose 动画艺术探索之瞅下 Compose 的动画
  • Compose 动画艺术探索之可见性动画

Compose的属性动画

属性动画是通过不断地修改值来实现的,而初始值和结束值之间的过渡动画就需要来计算了。在 Compose 中为我们提供了一整套 api 来实现属性动画,具体有哪些呢?让我们一起来看下吧!

在这里插入图片描述

官方为我们提供了上图这十种方法,我们可以根据实际项目中的需求进行挑选使用。

在第一篇文章中也提到了 Compose 的属性动画,但只是简单使用了下,告诉大家 Compose 有这个东西,今天咱们来具体看下!

先来看下 animateColorAsState 的代码吧:

@Composable
fun animateColorAsState(targetValue: Color,animationSpec: AnimationSpec<Color> = colorDefaultSpring,label: String = "ColorAnimation",finishedListener: ((Color) -> Unit)? = null
): State<Color> {val converter = remember(targetValue.colorSpace) {(Color.VectorConverter)(targetValue.colorSpace)}return animateValueAsState(targetValue, converter, animationSpec, label = label, finishedListener = finishedListener)
}

可以看到一共接收四个参数,来分别看下代表什么吧:

  • targetValue:顾名思义,目标值,这里对应的就是想要转换成的颜色
  • animationSpec:动画规格,动画随着时间改变值的一种规格吧,上一篇文章中也提到了,但由于上一篇文章主要内容并不是这个,也就没有讲,本篇文章会详细说明
  • label:标签,以区别于其他动画
  • finishedListener:在动画完成时会进行回调

参数并不算多,而且有三个是可选参数,也就只有 targetValue 必须要进行设置。方法体内只通过 Color.colorSpace 强转构建了一个 TwoWayConverter

前面说过,大多数 Compose 动画 API 支持将 FloatColorDp 以及其他基本数据类型作为 开箱即用的动画值,但有时我们需要为其他数据类型(比如自定义类型)添加动画效果。在动画播放期间,任何动画值都表示为 AnimationVector。使用相应的 TwoWayConverter 即可将值转换为 AnimationVector,反之亦然,这样一来,核心动画系统就可以统一对其进行处理了。由于颜色有 argb,所以构建的时候使用的是 AnimationVector4D ,来看下吧:

val Color.Companion.VectorConverter:(colorSpace: ColorSpace) -> TwoWayConverter<Color, AnimationVector4D>get() = ColorToVector

如果按照我之前的习惯肯定要接着看 animateValueAsState 方法内部的代码了,但今天等会再看!再来看看 animateDpAsState 的代码吧!

@Composable
fun animateDpAsState(targetValue: Dp,animationSpec: AnimationSpec<Dp> = dpDefaultSpring,label: String = "DpAnimation",finishedListener: ((Dp) -> Unit)? = null
): State<Dp> {return animateValueAsState(targetValue,Dp.VectorConverter,animationSpec,label = label,finishedListener = finishedListener)
}

发现了点什么没有,参数基本一摸一样,别着急,咱们再看看别的!

@Composable
fun animateIntAsState(targetValue: Int,animationSpec: AnimationSpec<Int> = intDefaultSpring,label: String = "IntAnimation",finishedListener: ((Int) -> Unit)? = null
)
​
@Composable
fun animateSizeAsState(targetValue: Size,animationSpec: AnimationSpec<Size> = sizeDefaultSpring,label: String = "SizeAnimation",finishedListener: ((Size) -> Unit)? = null
)
​
@Composable
fun animateRectAsState(targetValue: Rect,animationSpec: AnimationSpec<Rect> = rectDefaultSpring,label: String = "RectAnimation",finishedListener: ((Rect) -> Unit)? = null
)

不能说是大同小异,只能说是一摸一样!既然一摸一样的话咱们就以文章开头的 animateColorAsState 来看吧!

上面的说法其实是不对的,并不是有十种,而是九种,因为九种都调用了 animateValueAsState ,其实也可以说有无数种,因为可以自定义。。。。

参数

下面先来看下 animateValueAsState 的方法体吧:

@Composable
fun <T, V : AnimationVector> animateValueAsState(targetValue: T,typeConverter: TwoWayConverter<T, V>,animationSpec: AnimationSpec<T> = remember { spring() },visibilityThreshold: T? = null,label: String = "ValueAnimation",finishedListener: ((T) -> Unit)? = null
): State<T>

来看看接收的参数吧,可以发现有两个参数没有见过:

  • typeConverter:类型转换器,将需要的类型转换为 AnimationVector
  • visibilityThreshold:一个可选的阈值,用于定义何时动画值可以被认为足够接近targetValue以结束动画

OK,剩下的参数在上面都介绍过,就不重复进行介绍了。

方法体

由于 animateValueAsState 方法有点长,所以分开来看吧,接下来看下 animateValueAsState 方法中的前半部分:

val animatable = remember { Animatable(targetValue, typeConverter, visibilityThreshold, label) }
val listener by rememberUpdatedState(finishedListener)
val animSpec: AnimationSpec<T> by rememberUpdatedState(animationSpec.run {if (visibilityThreshold != null && this is SpringSpec &&this.visibilityThreshold != visibilityThreshold) {spring(dampingRatio, stiffness, visibilityThreshold)} else {this}}
)
val channel = remember { Channel<T>(Channel.CONFLATED) }
SideEffect {channel.trySend(targetValue)
}
LaunchedEffect(channel) {for (target in channel) {val newTarget = channel.tryReceive().getOrNull() ?: targetlaunch {if (newTarget != animatable.targetValue) {animatable.animateTo(newTarget, animSpec)listener?.invoke(animatable.value)}}}
}

可以看到首先构建了一个 Animatable ,然后记录了完成回调,又记录了 AnimationSpec ,之后有个判断,如果 visibilityThreshold 不为空并且 AnimationSpecSpringSpec 的时候为新构建的一个 AnimationSpec ,反之则还是传进来的 AnimationSpec

Animatable 是个啥呢?它是一个值容器,它可以在通过 animateTo 更改值时为值添加动画效果,它可确保一致的连续性和互斥性,这意味着值变化始终是连续的,并且会取消任何正在播放的动画。Animatable 的许多功能(包括 animateTo)以挂起函数的形式提供,所以需要封装在适当的协程作用域内,所以下面使用了 LaunchedEffect 来包裹执行 animateTo 方法,最后调用了动画完成的回调。

由于 Animatable 类中代码比较多,先来看下类的初始化及构造方法吧!

class Animatable<T, V : AnimationVector>(initialValue: T,val typeConverter: TwoWayConverter<T, V>,private val visibilityThreshold: T? = null,val label: String = "Animatable"
) 

可以看到这里使用到的参数在 animateValueAsState 中都有,就不一一介绍了,挑着重点来,来看看上面使用到的 animateTo 吧:

suspend fun animateTo(targetValue: T,animationSpec: AnimationSpec<T> = defaultSpringSpec,initialVelocity: T = velocity,block: (Animatable<T, V>.() -> Unit)? = null
): AnimationResult<T, V> {val anim = TargetBasedAnimation(animationSpec = animationSpec,initialValue = value,targetValue = targetValue,typeConverter = typeConverter,initialVelocity = initialVelocity)return runAnimation(anim, initialVelocity, block)
}

可以看到 animateTo 使用传进来的参数构建了一个 TargetBasedAnimation ,这是一个方便的动画包装类,适用于所有基于目标的动画,即具有预定义结束值的动画。然后返回调用了 runAnimation ,返回值为 AnimationResult ,来看下吧:

class AnimationResult<T, V : AnimationVector>(val endState: AnimationState<T, V>,val endReason: AnimationEndReason
) {override fun toString(): String = "AnimationResult(endReason=$endReason, endState=$endState)"
}

AnimationResult 在动画结尾包含关于动画的信息,endState 捕获动画在最后一帧的值 evelocityframe time 等。它可以用于启动另一个动画以从先前中断的动画继续速度。endReason 描述动画结束的原因。

下面看下 runAnimation 吧:

private suspend fun runAnimation(animation: Animation<T, V>,initialVelocity: T,block: (Animatable<T, V>.() -> Unit)?
): AnimationResult<T, V> {
​val startTime = internalState.lastFrameTimeNanosreturn mutatorMutex.mutate {try {......endState.animate(animation,startTime) {updateState(internalState)......}val endReason = if (clampingNeeded) BoundReached else FinishedendAnimation()AnimationResult(endState, endReason)} catch (e: CancellationException) {// Clean up internal states first, then throw.endAnimation()throw e}}
}

这里需要注意:所有不同类型的动画代码路径最终都会汇聚到这个方法中。

好了,基本快见到阳光了!

天亮了

上面方法中有一行:endState.animate ,这个是关键,来看下!

internal suspend fun <T, V : AnimationVector> AnimationState<T, V>.animate(animation: Animation<T, V>,startTimeNanos: Long = AnimationConstants.UnspecifiedTime,block: AnimationScope<T, V>.() -> Unit = {}
) {val initialValue = animation.getValueFromNanos(0)val initialVelocityVector = animation.getVelocityVectorFromNanos(0)var lateInitScope: AnimationScope<T, V>? = nulltry {if (startTimeNanos == AnimationConstants.UnspecifiedTime) {val durationScale = coroutineContext.durationScaleanimation.callWithFrameNanos {lateInitScope = AnimationScope(...).apply {// 第一帧doAnimationFrameWithScale(it, durationScale, animation, this@animate, block)}}} else {lateInitScope = AnimationScope(...).apply {// 第一帧doAnimationFrameWithScale()}}// 后续帧while (lateInitScope!!.isRunning) {val durationScale = coroutineContext.durationScaleanimation.callWithFrameNanos {lateInitScope!!.doAnimationFrameWithScale(it, durationScale, animation, this, block)}}// 动画结束} catch (e: CancellationException) {lateInitScope?.isRunning = falseif (lateInitScope?.lastFrameTimeNanos == lastFrameTimeNanos) {isRunning = false}throw e}
}

嗯,柳暗花明!这个动画函数从头到尾运行给定 animation 中定义的动画。在动画过程中,AnimationState 将被更新为最新的值,速度,帧时间等。

到这里 animateColorAsState 大概过了一遍,但也只是简单走了一遍流程,并没有深究里面的细节,比如 Animatable 类中都没看,runAnimation 方法也只是看了主要的代码等等。

结尾

本篇文章先写到这里吧,属性动画其实都差不多,区别只是泛型不同以及一些特定实现,大家如果有需要可以一个一个去看看。

本文所有源码基于 Compose 1.3.0-beta02

本文至此结束,有用的地方大家可以参考,当然如果能帮助到大家,哪怕是一点也足够了。就这样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_229661.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TensorFlow之文本分类算法-6

1 前言 2 收集数据 3 探索数据 4 选择模型 5 准备数据 6 模型-构建训练评估 构建输出层 构建n-gram模型 构建序列模型 GloVe&#xff08;英文全称是Global Vectors for Word Representation&#xff09;是一个全球化的英语语境的单词表示的向量集&#xff0c;其使用非…

Windows ssh免密访问Linux服务器

文章目录1.在Windows上生成公钥和私钥2.将公钥中的内容复制到linux服务器3.确认linux服务器开启了允许SSH免密登录4.确认免密登录配置成功ssh提供了安全的身份认证的策略&#xff0c;在免密登录之前&#xff0c;首先需要一对公钥和私钥。客户端拿着私钥&#xff0c;服务端拿着公…

HTML网页制作代码——简约的旅游图文相册博客HTML模板(12页)HTML+CSS+JavaScript 静态HTML旅行主题网页作业

&#x1f468;‍&#x1f393;学生HTML静态网页基础水平制作&#x1f469;‍&#x1f393;&#xff0c;页面排版干净简洁。使用HTMLCSS页面布局设计,web大学生网页设计作业源码&#xff0c;这是一个不错的旅游网页制作&#xff0c;画面精明&#xff0c;排版整洁&#xff0c;内容…

了解世界杯赔率,让您运气更‘好‘(个人分享)

足球世界杯买球赢面计算理论基础实际计算用例&#xff1a;代码实现理论基础 假设有两只球队甲和乙&#xff0c;在双方实力局等的情况下&#xff0c;赢球概率都为0.5%&#xff0c;则有&#xff1a; 甲乙概率胜负1/4胜胜1/4负胜1/4负负1/4 由此可知&#xff1a;甲胜的概率是1/4…

亚马逊云科技推出安全数据湖Amazon Security Lake

2022年12月2日&#xff0c;亚马逊云科技在2022 re:Invent全球大会上宣布&#xff0c;推出Amazon Security Lake&#xff0c;该服务可以自动将客户在云端和本地的安全数据集中到客户在亚马逊云科技账户下专门构建的数据湖中&#xff0c;方便客户针对安全数据做出快速行动。 Am…

教你6招轻松搞定 网站被木马反复篡改

提到网络被恶意篡改&#xff0c;应该让很多做了百度竞价的企业官网怀恨已久了吧&#xff1f;这类行为的目的就是通过这些受害网站获得排名并跳转到违法网站&#xff0c;达到不法的目的。对于企业来说不但损失了百度竞价的费用&#xff0c;还对企业形象造成很大的影响。甚至直接…

[附源码]计算机毕业设计springboot云南美食管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

svg路径动画

前言 最近在开发大屏看板&#xff0c;UI让做一个这样的效果 本来也简单&#xff0c;UI给个git动图放上就好了。但是UI给的图有四五十m&#xff0c;实在是太大了。后来想到了svg路径动画&#xff0c;之前从来没有搞过&#xff0c;就研究了下&#xff0c;由于svg没怎么研究过&a…

实现自定义Spring Boot Starter

实现自定义Spring Boot Starter一、原理二、实战1 自定义 Spring Boot Starter1.1 添加maven依赖1.2 属性类AuthorProperties1.3 自动配置类AuthorAutoConfiguration1.4 业务逻辑AuthorServer1.5 spring.factories2 测试自定义的 Spring Boot Starter2.1 新建module或者新建工程…

Compose 动画艺术探索之动画规格

本篇文章是此专栏的第四篇文章&#xff0c;如果想阅读前三篇文章的话请点击下方链接&#xff1a; Compose 动画艺术探索之瞅下 Compose 的动画Compose 动画艺术探索之可见性动画Compose 动画艺术探索之属性动画 动画规格在上一篇文章中提到过&#xff0c;不过上一篇文章中说的…

[附源码]JAVA毕业设计教材管理(系统+LW)

[附源码]JAVA毕业设计教材管理&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xf…

ARM mkv210_image.c 文件详解

一、mkv210_image.c 的使用演示 裸机程序中的 Makefile&#xff08;实际上真正的项目的 Makefile 都是这样的&#xff09;是把程序的编译和链接过程分开的。&#xff08;平时我们用 gcc a.c -o exe 这种方式来编译时&#xff0c;实际上把编译和链接过程一步完成了。在内部实际…

[附源码]Python计算机毕业设计Django教学辅助系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

一文教会你如何在内网搭建一套属于自己小组的在线 API 文档?

Hello&#xff0c;大家好&#xff0c;我是阿粉&#xff0c;对接文档是每个开发人员不可避免都要写的&#xff0c;友好的文档可以大大的提升工作效率。 阿粉最近将项目的文档基于 Gitbook 和 Gitlab 的 Webhook 功能的在内网部署了一套实时的&#xff0c;使用起来特方便了。跟着…

[附源码]计算机毕业设计JAVA校园拓展活动管理系统

[附源码]计算机毕业设计JAVA校园拓展活动管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM my…

什么是【固件】?

文章目录一、软件 硬件 固件二、BIOS&#xff08;Basic Input/output System&#xff09;三、百度百科的解释四、固件的工作原理五、应用六、参考链接一、软件 硬件 固件 通常我们会将硬件和软件分开看待&#xff0c;二者协同工作为我们提供计算机的体验。硬件是摸得着的实体&…

SpringBoot中使用MySQL存用户信息, 日志的使用

SpringBoot中使用MySQL存用户信息 UserController类 package com.tedu.secboot.controller; import com.tarena.mnmp.api.SendParam; import com.tedu.secboot.entity.User; import com.tedu.secboot.util.DBUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory;…

[附源码]计算机毕业设计springboot在线图书销售系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

2023年天津天狮学院专升本市场营销专业《管理学》考试大纲

2023天津天狮学院高职升本科市场营销专业入学考试《管理学》考试大纲一、考试性质 《管理学》专业课程考试是天津天狮学院市场营销专业高职升本入学考试的必考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的选拔性考试。《管理学》考试大纲的编…

LIO-SAM源码解析(四):imuPreintegration.cpp

1. 代码流程 2. 功能说明 这个cpp文件主要有两个类&#xff0c;一个叫IMUPreintegration类&#xff0c;一个叫TransformFusion类。 现在我们分开讲&#xff0c;先说IMUPreintegration类。 关于IMU原始数据&#xff0c;送入imuhandle中&#xff1a; 2.1. imuhandle imu原始…