欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Compose?動畫藝術(shù)之屬性動畫探索

 更新時間:2022年09月30日 09:08:37   作者:Zhujiang  
這篇文章主要介紹了Compose動畫藝術(shù)之屬性動畫探索,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言

本篇文章是此專欄的第三篇文章,如果想閱讀前兩篇文章的話請點擊下方鏈接:

Compose的屬性動畫

屬性動畫是通過不斷地修改值來實現(xiàn)的,而初始值和結(jié)束值之間的過渡動畫就需要來計算了。在 Compose 中為我們提供了一整套 api 來實現(xiàn)屬性動畫,具體有哪些呢?讓我們一起來看下吧!

官方為我們提供了上圖這十種方法,我們可以根據(jù)實際項目中的需求進行挑選使用。

在第一篇文章中也提到了 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
 ?  )
}

可以看到一共接收四個參數(shù),來分別看下代表什么吧:

  • targetValue:顧名思義,目標值,這里對應(yīng)的就是想要轉(zhuǎn)換成的顏色
  • animationSpec:動畫規(guī)格,動畫隨著時間改變值的一種規(guī)格吧,上一篇文章中也提到了,但由于上一篇文章主要內(nèi)容并不是這個,也就沒有講,本篇文章會詳細說明
  • label:標簽,以區(qū)別于其他動畫
  • finishedListener:在動畫完成時會進行回調(diào)

參數(shù)并不算多,而且有三個是可選參數(shù),也就只有 targetValue 必須要進行設(shè)置。方法體內(nèi)只通過 Color.colorSpace 強轉(zhuǎn)構(gòu)建了一個 TwoWayConverter 。

前面說過,大多數(shù) Compose 動畫 API 支持將 Float、Color、Dp 以及其他基本數(shù)據(jù)類型作為 開箱即用的動畫值,但有時我們需要為其他數(shù)據(jù)類型(比如自定義類型)添加動畫效果。在動畫播放期間,任何動畫值都表示為 AnimationVector。使用相應(yīng)的 TwoWayConverter 即可將值轉(zhuǎn)換為 AnimationVector,反之亦然,這樣一來,核心動畫系統(tǒng)就可以統(tǒng)一對其進行處理了。由于顏色有 argb,所以構(gòu)建的時候使用的是 AnimationVector4D ,來看下吧:

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

如果按照我之前的習慣肯定要接著看 animateValueAsState 方法內(nèi)部的代碼了,但今天等會再看!再來看看 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
 ?  )
}

發(fā)現(xiàn)了點什么沒有,參數(shù)基本一摸一樣,別著急,咱們再看看別的!

@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 來看吧!

上面的說法其實是不對的,并不是有十種,而是九種,因為九種都調(diào)用了 animateValueAsState ,其實也可以說有無數(shù)種,因為可以自定義。。。。

參數(shù)

下面先來看下 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>

來看看接收的參數(shù)吧,可以發(fā)現(xiàn)有兩個參數(shù)沒有見過:

  • typeConverter:類型轉(zhuǎn)換器,將需要的類型轉(zhuǎn)換為 AnimationVector
  • visibilityThreshold:一個可選的閾值,用于定義何時動畫值可以被認為足夠接近targetValue以結(jié)束動畫

OK,剩下的參數(shù)在上面都介紹過,就不重復(fù)進行介紹了。

方法體

由于 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() ?: target
 ? ? ? ?launch {
 ? ? ? ? ? ?if (newTarget != animatable.targetValue) {
 ? ? ? ? ? ? ? ?animatable.animateTo(newTarget, animSpec)
 ? ? ? ? ? ? ? ?listener?.invoke(animatable.value)
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}

可以看到首先構(gòu)建了一個 Animatable ,然后記錄了完成回調(diào),又記錄了 AnimationSpec ,之后有個判斷,如果 visibilityThreshold 不為空并且 AnimationSpec 為 SpringSpec 的時候為新構(gòu)建的一個 AnimationSpec ,反之則還是傳進來的 AnimationSpec 。

那 Animatable 是個啥呢?它是一個值容器,它可以在通過 animateTo 更改值時為值添加動畫效果,它可確保一致的連續(xù)性和互斥性,這意味著值變化始終是連續(xù)的,并且會取消任何正在播放的動畫。Animatable 的許多功能(包括 animateTo)以掛起函數(shù)的形式提供,所以需要封裝在適當?shù)膮f(xié)程作用域內(nèi),所以下面使用了 LaunchedEffect 來包裹執(zhí)行 animateTo 方法,最后調(diào)用了動畫完成的回調(diào)。

由于 Animatable 類中代碼比較多,先來看下類的初始化及構(gòu)造方法吧!

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

可以看到這里使用到的參數(shù)在 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 使用傳進來的參數(shù)構(gòu)建了一個 TargetBasedAnimation ,這是一個方便的動畫包裝類,適用于所有基于目標的動畫,即具有預(yù)定義結(jié)束值的動畫。然后返回調(diào)用了 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 在動畫結(jié)尾包含關(guān)于動畫的信息,endState 捕獲動畫在最后一幀的值 evelocityframe time 等。它可以用于啟動另一個動畫以從先前中斷的動畫繼續(xù)速度。endReason 描述動畫結(jié)束的原因。

下面看下 runAnimation 吧:

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

這里需要注意:所有不同類型的動畫代碼路徑最終都會匯聚到這個方法中。

好了,基本快見到陽光了!

天亮了

上面方法中有一行:endState.animate ,這個是關(guān)鍵,來看下!

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>? = null
 ? ?try {
 ? ? ? ?if (startTimeNanos == AnimationConstants.UnspecifiedTime) {
 ? ? ? ? ? ?val durationScale = coroutineContext.durationScale
 ? ? ? ? ? ?animation.callWithFrameNanos {
 ? ? ? ? ? ? ? ?lateInitScope = AnimationScope(...).apply {
 ? ? ? ? ? ? ? ? ? ?// 第一幀
 ? ? ? ? ? ? ? ? ? ?doAnimationFrameWithScale(it, durationScale, animation, this@animate, block)
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }
 ? ? ?  } else {
 ? ? ? ? ? ?lateInitScope = AnimationScope(...).apply {
 ? ? ? ? ? ? ? ?// 第一幀
 ? ? ? ? ? ? ? ?doAnimationFrameWithScale()
 ? ? ? ? ?  }
 ? ? ?  }
 ? ? ? ?// 后續(xù)幀
 ? ? ? ?while (lateInitScope!!.isRunning) {
 ? ? ? ? ? ?val durationScale = coroutineContext.durationScale
 ? ? ? ? ? ?animation.callWithFrameNanos {
 ? ? ? ? ? ? ? ?lateInitScope!!.doAnimationFrameWithScale(it, durationScale, animation, this, block)
 ? ? ? ? ?  }
 ? ? ?  }
 ? ? ? ?// 動畫結(jié)束
 ?  } catch (e: CancellationException) {
 ? ? ? ?lateInitScope?.isRunning = false
 ? ? ? ?if (lateInitScope?.lastFrameTimeNanos == lastFrameTimeNanos) {
 ? ? ? ? ? ?isRunning = false
 ? ? ?  }
 ? ? ? ?throw e
 ?  }
}

嗯,柳暗花明!這個動畫函數(shù)從頭到尾運行給定 animation 中定義的動畫。在動畫過程中,AnimationState 將被更新為最新的值,速度,幀時間等。

到這里 animateColorAsState 大概過了一遍,但也只是簡單走了一遍流程,并沒有深究里面的細節(jié),比如 Animatable 類中都沒看,runAnimation 方法也只是看了主要的代碼等等。

結(jié)尾

到此這篇關(guān)于Compose 動畫藝術(shù)之屬性動畫探索的文章就介紹到這了,更多相關(guān)Compose屬性動畫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論