自訂動畫

許多 Animation API 通常都接受參數來自訂動畫行為。

使用 AnimationSpec 參數自訂動畫

多數的動畫 API 都允許開發人員透過選用的 AnimationSpec 參數來自訂動畫規格。

val alpha: Float by animateFloatAsState(
    targetValue = if (enabled) 1f else 0.5f,
    // Configure the animation duration and easing.
    animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing),
    label = "alpha"
)

AnimationSpec 有多種類型,可用於建立不同類型的動畫。

使用 spring 建立以物理為基礎的動畫

spring 可在起始值和結束值之間建立以物理特性為基礎的動畫,並接受 2 個參數:dampingRatiostiffness

dampingRatio 可定義彈簧的彈性,預設值為 Spring.DampingRatioNoBouncy

圖 1. 設定不同的彈簧阻尼比率。

stiffness 可定義彈簧朝結束值移動的速度,預設值為 Spring.StiffnessMedium

圖 2. 設定不同的彈簧剛度

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioHighBouncy,
        stiffness = Spring.StiffnessMedium
    ),
    label = "spring spec"
)

與以持續時間為基礎的 AnimationSpec 類型相比,spring 可在目標值於動畫期間發生變化時確保速率的持續性,因此能更順暢地處理中斷情形。許多動畫 API (例如 animate*AsStateupdateTransition) 會使用 spring 做為預設的 AnimationSpec。

舉例來說,如果我們將 spring 設定套用至由使用者觸控驅動的以下動畫,當動畫播放時中斷,您會發現使用 tween 的回應速度不如使用 spring 時流暢。

圖 3. 設定動畫的 tweenspring 規格,並中斷動畫。

使用緩和曲線和 tween 在起始值和結束值之間建立動畫

tween 會使用緩和曲線,透過指定的 durationMillis 於起始值和結束值之間建立動畫效果。tween 是「between」一詞的縮寫,表示兩個值之間的

您也可以指定 delayMillis 來延遲動畫的開始時間。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = tween(
        durationMillis = 300,
        delayMillis = 50,
        easing = LinearOutSlowInEasing
    ),
    label = "tween delay"
)

詳情請參閱「Easing」。

使用 keyframes 在特定時間點為特定值建立動畫

keyframes 會根據動畫期間不同時間戳記中指定的快照值建立動畫效果。在任何指定的時間,系統會在兩個主要畫面格值之間內插動畫值。針對對每個主要畫面格,都能指定 Easing 來決定內插曲線。

您可以選擇將在 0 毫秒和持續時間處指定值。如果不指定這些值,這些值會分別預設為動畫的起始值和結束值。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = keyframes {
        durationMillis = 375
        0.0f at 0 using LinearOutSlowInEasing // for 0-15 ms
        0.2f at 15 using FastOutLinearInEasing // for 15-75 ms
        0.4f at 75 // ms
        0.4f at 225 // ms
    },
    label = "keyframe"
)

使用 repeatable 重複動畫

repeatable 會重複執行以持續時間為基礎的動畫 (例如 tweenkeyframes),直到達到指定的疊代次數為止。您可以傳遞 repeatMode 參數以指定是否應該從開始 (RepeatMode.Restart) 或結束 (RepeatMode.Reverse) 開始重複播放動畫。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = repeatable(
        iterations = 3,
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ),
    label = "repeatable spec"
)

使用 infiniteRepeatable 無限重複動畫

infiniteRepeatablerepeatable 類似,但會重複無限次的疊代。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ),
    label = "infinite repeatable"
)

在使用 ComposeTestRule 的測試中,系統不會執行使用 infiniteRepeatable 的動畫,而會使用每個動畫值的初始值來轉譯這個元件。

使用 snap 立即對齊結束值

snap 是一種特殊的 AnimationSpec,可將值立即切換到結束值。您可以指定 delayMillis 以便延遲動畫的開始時間。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = snap(delayMillis = 50),
    label = "snap spec"
)

設定自訂轉場函式

以持續時間為基礎的 AnimationSpec 作業 (例如 tweenkeyframes) 會使用 Easing 來調整動畫的分數。如此一來,動畫值就能加速或減速,而不是以固定速率移動。分數是介於 0 (起始) 和 1.0 (結束) 之間的值,表示動畫中的目前點。

Easing 實際上是一種函式,可接受介於 0 和 1.0 之間的分數值並傳回浮點值。傳回的值可能在範圍邊界外,代表過衝或是下衝。您可以建立自訂的 Easing,如以下程式碼所示。

val CustomEasing = Easing { fraction -> fraction * fraction }

@Composable
fun EasingUsage() {
    val value by animateFloatAsState(
        targetValue = 1f,
        animationSpec = tween(
            durationMillis = 300,
            easing = CustomEasing
        ),
        label = "custom easing"
    )
    // ……
}

Compose 提供多種內建 Easing 函式,可滿足大多數用途的需求。如要進一步瞭解如何根據您的情境使用哪些 Easing,請參閱「速度 - 質感設計」。

  • FastOutSlowInEasing
  • LinearOutSlowInEasing
  • FastOutLinearEasing
  • LinearEasing
  • CubicBezierEasing
  • 顯示更多資訊

透過轉換自訂資料類型來製作動畫 (轉換至 AnimationVector 和從 AnimationVector 轉換)

多數 Compose 動畫 API 預設支援 FloatColorDp 和其他基本資料類型做為動畫值,但有時您需要為其他資料類型 (包括您的自訂類型) 建立動畫。在動畫播放期間,任何動畫值都會以 AnimationVector 表示。使用對應的 TwoWayConverter 即可將值轉換為 AnimationVector,反之亦然。這樣以來,核心動畫系統就能統一處理這些內容。舉例來說,Int 是以包含單一浮點值的 AnimationVector1D 代表。「Int」的「TwoWayConverter」看起來會像是這樣:

val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
    TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })

基本上,Color 是紅色、綠色、藍色和 alpha 這 4 個值的組合,因此 Color 可轉換成包含 4 個浮點值的 AnimationVector4D。透過這種方式,動畫中使用的每種資料類型都會轉換成 AnimationVector1DAnimationVector2DAnimationVector3DAnimationVector4D,視其維度而定。如此一來,您就能針對物件的不同元件個別建立動畫效果,而每個元件都有各自的速率追蹤。基本資料類型的內建轉換器可使用 Color.VectorConverterDp.VectorConverter 等轉換器存取。

如要想要為新的資料類型新增支援以作為動畫值,您可以建立專屬的 TwoWayConverter 並將其提供給 API。舉例來說,您可以使用 animateValueAsState 為自訂資料類型建立動畫效果,如下所示:

data class MySize(val width: Dp, val height: Dp)

@Composable
fun MyAnimation(targetSize: MySize) {
    val animSize: MySize by animateValueAsState(
        targetSize,
        TwoWayConverter(
            convertToVector = { size: MySize ->
                // Extract a float value from each of the `Dp` fields.
                AnimationVector2D(size.width.value, size.height.value)
            },
            convertFromVector = { vector: AnimationVector2D ->
                MySize(vector.v1.dp, vector.v2.dp)
            }
        ),
        label = "size"
    )
}

下列清單包含一些內建的 VectorConverter