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

Jetpack Compose自定義動(dòng)畫(huà)與Animatable詳解

 更新時(shí)間:2022年10月28日 09:27:35   作者:唯鹿  
在今年的Google/IO大會(huì)上,亮相了一個(gè)全新的 Android 原生 UI 開(kāi)發(fā)框架-Jetpack Compose, 與蘋(píng)果的SwiftIUI一樣,Jetpack Compose是一個(gè)聲明式的UI框架,這篇文章主要介紹了Jetpack Compose自定義動(dòng)畫(huà)與Animatable

本篇主要是自定義動(dòng)畫(huà)與Animatable

AnimationSpec

上一篇中,出現(xiàn)了多次animationSpec屬性,它是用來(lái)自定義動(dòng)畫(huà)規(guī)范的。例如:

fun Modifier.animateContentSize(
    animationSpec: FiniteAnimationSpec<IntSize> = spring(),
    finishedListener: ((initialValue: IntSize, targetValue: IntSize) -> Unit)? = null
): Modifier

下面是幾種自帶的動(dòng)畫(huà)規(guī)范:

1.spring

我們上一篇也簡(jiǎn)單說(shuō)到了,它是一種彈性動(dòng)畫(huà)。源碼如下:

@Stable
fun <T> spring(
    dampingRatio: Float = Spring.DampingRatioNoBouncy,
    stiffness: Float = Spring.StiffnessMedium,
    visibilityThreshold: T? = null
): SpringSpec<T> = SpringSpec(dampingRatio, stiffness, visibilityThreshold)

dampingRatio: 定義彈簧的彈性,數(shù)值越小彈性越高。默認(rèn)值DampingRatioNoBouncy為1f。下圖為其他數(shù)值的效果:

  • stiffness: 定義彈簧向結(jié)束值移動(dòng)的速度。默認(rèn)值StiffnessMedium為1500f。
  • visibilityThreshold:可見(jiàn)臨界值(動(dòng)畫(huà)停止的值)。比如目標(biāo)150dp,此值設(shè)置10dp,那么回彈距離小于這個(gè)值時(shí)就會(huì)停止彈性效果。

2.tween

在指定的時(shí)間內(nèi)使用緩和曲線在起始值和結(jié)束值之間添加動(dòng)畫(huà)效果。

@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:動(dòng)畫(huà)的持續(xù)時(shí)間,默認(rèn)值300毫秒。
  • delayMillis:延遲動(dòng)畫(huà)開(kāi)始的時(shí)間。
  • easing:兩個(gè)值之間的插值曲線,類似Android中的Interpolator插值器。目前compose中提供了五種類型,這里就不一一說(shuō)明了,實(shí)際使用時(shí)可以嘗試一下。當(dāng)然你也可以使用CubicBezierEasing甚至Easing自定義曲線。

3.keyframes

在動(dòng)畫(huà)時(shí)長(zhǎng)內(nèi)的不同時(shí)間戳中指定快照值,實(shí)現(xiàn)動(dòng)畫(huà)效果。在任何給定時(shí)間,動(dòng)畫(huà)值都將插值到兩個(gè)關(guān)鍵幀值之間。對(duì)于其中每個(gè)關(guān)鍵幀,您都可以指定 Easing 來(lái)確定插值曲線。

@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和結(jié)束值1,動(dòng)畫(huà)時(shí)長(zhǎng)375毫秒。

  • 從0開(kāi)始到0.2結(jié)束,前15毫秒使用LinearOutSlowInEasing過(guò)渡。
  • 從0.2開(kāi)始到0.4結(jié)束,15到75毫秒使用FastOutLinearInEasing過(guò)渡。
  • 從0.4開(kāi)始到0.4結(jié)束,75到225毫秒勻速過(guò)渡。(靜止效果)
  • 從0.4開(kāi)始到1結(jié)束,225毫秒到結(jié)束勻速過(guò)渡。

效果圖如下:

keyframes同樣也可以設(shè)置delayMillis指定延時(shí)時(shí)間。

4.repeatable

重復(fù)運(yùn)行基于時(shí)長(zhǎng)的動(dòng)畫(huà),直至達(dá)到指定的迭代計(jì)數(shù)。

@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,動(dòng)畫(huà)運(yùn)行的次數(shù)。1表示不重復(fù)。建議使用infiniteRepeatable創(chuàng)建無(wú)限重復(fù)的動(dòng)畫(huà)。
  • animation,設(shè)置需要重復(fù)的動(dòng)畫(huà)。
  • repeatMode,重復(fù)模式。指定動(dòng)畫(huà)是從頭開(kāi)始 (RepeatMode.Restart) 還是從結(jié)尾開(kāi)始 (RepeatMode.Reverse) 重復(fù)播放。
  • initialStartOffset,可用于延遲動(dòng)畫(huà)的開(kāi)始或快進(jìn)動(dòng)畫(huà)到給定的播放時(shí)間。如果設(shè)置StartOffset(1500)就是延遲動(dòng)畫(huà)。 設(shè)置StartOffset(1500, offsetType = StartOffsetType.FastForward)就是快進(jìn)動(dòng)畫(huà)。默認(rèn)情況下,延時(shí)時(shí)長(zhǎng)為0。

如果需要?jiǎng)赢?huà)重復(fù)無(wú)限次,可以使用infiniteRepeatable,用法與repeatable一致。

5.snap

snap是特殊的 AnimationSpec,它會(huì)立即將值切換到結(jié)束值。您可以指定 delayMillis 來(lái)延遲動(dòng)畫(huà)播放的開(kāi)始時(shí)間。

@Stable
fun <T> snap(delayMillis: Int = 0) = SnapSpec<T>(delayMillis)

Animatable

Animatable 是一個(gè)值容器,它可以在通過(guò) animateTo 更改值時(shí)為值添加動(dòng)畫(huà)效果。它可確保一致的連續(xù)性和互斥性,這意味著值變化始終是連續(xù)的,并且會(huì)取消任何正在播放的動(dòng)畫(huà)。

Animatable 的許多功能(包括 animateTo)以掛起函數(shù)的形式提供。這意味著,它們需要封裝在適當(dāng)?shù)膮f(xié)程作用域內(nèi)。例如,您可以使用 LaunchedEffect 可組合項(xiàng)針對(duì)指定鍵值的時(shí)長(zhǎng)創(chuàng)建一個(gè)作用域。

// 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))

在上面的示例中,我們創(chuàng)建并記住了初始值為 Color.GrayAnimatable 實(shí)例。根據(jù)布爾標(biāo)記 ok 的值,顏色將以動(dòng)畫(huà)形式呈現(xiàn) Color.GreenColor.Red。對(duì)該布爾值的任何后續(xù)更改都會(huì)使動(dòng)畫(huà)開(kāi)始使用另一種顏色。如果更改該值時(shí)有正在播放的動(dòng)畫(huà),系統(tǒng)會(huì)取消該動(dòng)畫(huà),并且新動(dòng)畫(huà)將以當(dāng)前速度從當(dāng)前快照值開(kāi)始播放。

animate*AsState 相比,使用 Animatable 可以直接對(duì)以下幾個(gè)方面進(jìn)行更精細(xì)的控制。首先,Animatable 的初始值可以與第一個(gè)目標(biāo)值不同。例如,上面的代碼示例首先顯示一個(gè)灰色框,然后立即開(kāi)始通過(guò)動(dòng)畫(huà)呈現(xiàn)為綠色或紅色。其次,Animatable 對(duì)內(nèi)容值提供更多操作(即 snapToanimateDecay)。snapTo 可立即將當(dāng)前值設(shè)為目標(biāo)值。如果動(dòng)畫(huà)本身不是唯一的可信來(lái)源,且必須與其他狀態(tài)(如觸摸事件)同步,該函數(shù)就非常有用。animateDecay 用于啟動(dòng)播放從給定速度變慢的動(dòng)畫(huà)(衰減)。這有助于實(shí)現(xiàn)投擲行為。

下面看一個(gè)官方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)
            )
        }
    }

效果圖這里我就不放了。就是頁(yè)面上有個(gè)40*40dp的圓形view,點(diǎn)擊屏幕任意位置,他就會(huì)移動(dòng)過(guò)去。這里核心靠的就是animateTo方法,而且無(wú)論我們中間如果快速點(diǎn)擊不同位置,系統(tǒng)會(huì)取消上一個(gè)動(dòng)畫(huà),接著當(dāng)前位置移動(dòng)到目標(biāo)位置。

從上面這張圖可以看出各個(gè)API之間的關(guān)系。所有這些 API 都基于更基礎(chǔ)的 Animation API。雖然大多數(shù)應(yīng)用不會(huì)直接使用 Animation ,但 Animation 的某些自定義功能可以通過(guò)更高級(jí)別的 API 獲得。

到此,Compose的動(dòng)畫(huà)部分大體結(jié)束。

參考

Compose官方文檔

Compose官方文檔 – Animatable

到此這篇關(guān)于Jetpack Compose自定義動(dòng)畫(huà)與Animatable詳解的文章就介紹到這了,更多相關(guān)Jetpack Compose自定義動(dòng)畫(huà)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論