Compose Animation:animate*AsState関数のサンプル

投稿日:  更新日:

animate*AsState関数は制御する値のタイプにより、数種類が準備されています。

どの関数も、animateValueAsStateが原型のラッパー関数であり、動作は同じです。

ここでは、各々の関数について、サンプルを示します。

また、animateValueAsStateのサンプルは、独自の関数を作る方法を紹介しています。

※環境:Android Studio Iguana | 2023.2.1
    Kotlin 1.9.0
    Compose Compiler 1.5.1
    androidx.compose.animation:* 1.5.0

スポンサーリンク

関数の種類

animate*AsState関数は制御する値のタイプにより、次のような種類が準備されています。

アニメーション関数タイプコメント
animateFloatAsStateFloat
animateIntAsStateInt
animateDpAsStateDp
animateOffsetAsStateOffsetx、yはFloat型
x、yは独立して制御可能
animateIntOffsetAsStateIntOffsetx、yはInt型
x、yは独立して制御可能
animateSizeAsStateSizewidth、heightはFloat型
width、heightは独立して制御可能
animateIntSizeAsStateIntSizewidth、heightはInt型
width、heightは独立して制御可能
animateColorAsStateColora、r、g、bは独立して制御可能
animateRectAsStateRectleft、top、right、bottomはFloat型
left、top、right、bottomは独立して制御可能
animateValueAsStateValue上記9つの原型になる関数

この中で、animateValueAsStateは原型になる関数です。他の関数はanimateValueAsStateのラッパー関数になります。

スポンサーリンク

例:animateFloat/IntAsState

FloatInt
private val StartAlphaF = 0.0f
private val EndAlphaF = 1.0f

@Preview_mdpi
@Composable
private fun AnimateFloatAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.Center
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _alpha = animateFloatAsState(
            targetValue = if(_toggle.value) EndAlphaF else StartAlphaF,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.leaf1),
            contentDescription = null,
            modifier = Modifier
                .background(color = Color.LightGray)
                .alpha(_alpha.value)
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}
private val StartAlphaI = 0x00
private val EndAlphaI = 0xff

@Preview_mdpi
@Composable
private fun AnimateIntAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.Center
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _alpha = animateIntAsState(
            targetValue = if(_toggle.value) EndAlphaI else StartAlphaI,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.leaf1),
            contentDescription = null,
            modifier = Modifier
                .background(color = Color.LightGray)
                .alpha(_alpha.value.toFloat() / 255.0f)
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}
スポンサーリンク

例:animateDpAsState

private val StartX = 20.dp
private val EndX = 260.dp

@Preview_mdpi
@Composable
private fun AnimateDpAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 100.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.CenterStart
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _posX = animateDpAsState(
            targetValue = if(_toggle.value) EndX else StartX,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.baseline_toys_black_36),
            contentDescription = null,
            modifier = Modifier
                .size(36.dp)
                .offset(x = _posX.value)
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}

※「関数の動作」で掲載したサンプルと同じ

スポンサーリンク

例:animateOffset/IntOffsetAsState

OffsetIntOffset
private val StartOffset = Offset(20.0f, 20.0f)  // Floatで表したDp値
private val EndOffset = Offset(260.0f, 180.0f)  // Floatで表したDp値

@Preview_mdpi
@Composable
private fun AnimateOffsetAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF))
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _offset = animateOffsetAsState(
            targetValue = if(_toggle.value) EndOffset else StartOffset,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.baseline_toys_black_36),
            contentDescription = null,
            modifier = Modifier
                .size(36.dp)    // ↓↓ offsetはDpを欲する ↓↓
                .offset(x = _offset.value.x.dp, y = _offset.value.y.dp)
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}
private val StartIntOffset = IntOffset(20, 20)  // Intで表したDp値
private val EndIntOffset = IntOffset(260, 180)  // Intで表したDp値

@Preview_mdpi
@Composable
private fun AnimateIntOffsetAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF))
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _offset = animateIntOffsetAsState(
            targetValue = if(_toggle.value) EndIntOffset else StartIntOffset,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.baseline_toys_black_36),
            contentDescription = null,
            modifier = Modifier
                .size(36.dp)    // ↓↓ offsetはPxを欲する ↓↓
                .offset { _offset.value * density }
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}

例:animateSize/IntSizeAsState

SizeIntSize
private val StartSize = Size(50.0f, 210.0f)  // Floatで表したDp値
private val EndSize = Size(200.0f, 150.0f)   // Floatで表したDp値

@Preview_mdpi
@Composable
private fun AnimateSizeAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.Center
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _size = animateSizeAsState(
            targetValue = if(_toggle.value) EndSize else StartSize,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.leaf2),
            contentDescription = null,
            contentScale = ContentScale.FillBounds,
            modifier = Modifier
                .size(width = _size.value.width.dp, height = _size.value.height.dp)
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}
private val StartIntSize = IntSize(50, 210)  // Intで表したDp値
private val EndIntSize = IntSize(200, 150)   // Intで表したDp値

@Preview_mdpi
@Composable
private fun AnimateIntSizeAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.Center
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _size = animateIntSizeAsState(
            targetValue = if(_toggle.value) EndIntSize else StartIntSize,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.leaf2),
            contentDescription = null,
            contentScale = ContentScale.FillBounds,
            modifier = Modifier
                .size(width = _size.value.width.dp, height = _size.value.height.dp)
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}
スポンサーリンク

例:animateColorAsState

private val StartColor = Color(0xffff0040)
private val EndColor = Color(0xff00ffc0)

@Preview_mdpi
@Composable
private fun AnimateColorAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.Center
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _color = animateColorAsState(
            targetValue = if(_toggle.value) EndColor else StartColor,
            animationSpec = tween(durationMillis = 1000)
        )
        Image(
            painter = painterResource(R.drawable.leaf1),
            contentDescription = null,
            modifier = Modifier
                .background(color = _color.value)
                .scale(0.8f)
                .clickable { _toggle.value = !_toggle.value }
        )
    }
}
スポンサーリンク

例:animateRectAsState

private val StartRect = Rect(                           // Floatで表したDp値
    Offset(x = 10.0f, y = 10.0f), Size(50.0f, 100.0f))
private val EndRect = Rect(                             // Floatで表したDp値
    Offset(x = 170.0f, y = 150.0f), Size(100.0f, 50.0f))

@Preview_mdpi
@Composable
private fun AnimateRectAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.Center
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _rect = animateRectAsState(
            targetValue = if(_toggle.value) EndRect else StartRect,
            animationSpec = tween(durationMillis = 1000)
        )
        Canvas(
            modifier = Modifier
                .size(280.dp, 210.dp)
                .background(color = Color.LightGray)
                .clickable { _toggle.value = !_toggle.value }
        ) {
            drawOval(
                color = Color.Red,
                topLeft = _rect.value.topLeft * density, // Dp->Px変換が必要
                size = _rect.value.size * density)       // Dp->Px変換が必要
        }
    }
}
スポンサーリンク

例:animateValueAsState

animateValueAsStateは原型になる関数です。他の関数はanimateValueAsStateのラッパー関数になります。

他の関数と同様にVectorConverterを定義すれば、独自のanimateXXXAsState関数が作成できます。

以下は、3軸(X,Y,Z)の回転を行うRoataeXYZ.VectorConverterの例です。

data class RotateXYZ(val rX: Int, val rY: Int, val rZ: Int) { companion object }

private val RotateXYZToVector: TwoWayConverter<RotateXYZ, AnimationVector3D> =
    TwoWayConverter(
        convertToVector = {
            AnimationVector3D(it.rX.toFloat(), it.rY.toFloat(), it.rZ.toFloat())
        },
        convertFromVector = {
            RotateXYZ(it.v1.roundToInt(), it.v2.roundToInt(), it.v3.roundToInt())
        }
    )

val RotateXYZ.Companion.VectorConverter: TwoWayConverter<RotateXYZ, AnimationVector3D>
    get() = RotateXYZToVector

制御する値の個数(1/2/3/4個)により、アニメエンジン(AnimationVector1D/2D/3D/4D)を切り替えて使用します。

※例はタイプの変換箇所を明確するために、あえてrX/rY/rZをInt型で作成しています。Floatにすれば、toFloatとroundToIntは不要です。

private val StartRotate = RotateXYZ(rX = 0, rY = 0, rZ = 0)
private val EndRotate = RotateXYZ(rX = 360, rY = 360, rZ = 360)

@Preview_mdpi
@Composable
private fun AnimateValueAsStateSample() {
    Box(
        modifier = Modifier
            .size(width = 320.dp, height = 240.dp)
            .background(color = Color(0xFFF0F0FF)),
        contentAlignment = Alignment.Center
    ) {
        val _toggle = remember { mutableStateOf(false) }
        val _rotate = animateValueAsState(
            targetValue = if(_toggle.value) EndRotate else StartRotate,
            animationSpec = tween(durationMillis = 2000),
            typeConverter = RotateXYZ.VectorConverter
        )
        Image(
            painter = painterResource(R.drawable.bike),
            contentDescription = null,
            modifier = Modifier
                .scale(0.7f)
                .clickable { _toggle.value = !_toggle.value }
                .graphicsLayer {
                    rotationX = _rotate.value.rX.toFloat()
                    rotationY = _rotate.value.rY.toFloat()
                    rotationZ = _rotate.value.rZ.toFloat()
                }
        )
    }
}
スポンサーリンク

関連記事:

Jetpack Composeが提供するアニメーションAPIは非常に充実しています。 「どのAPIを使えば最適なのか?」と、その選択を迷うくらいに数が多いです。ドキュメントは、「〇〇占い」に登場するようなYes/Noの設問ツリーを掲載(アニメーションAPIを選択する)して、選択の手助け行っています。 ここでは、アニメーションAPIの中から「animate*AsState(*は型名が入る)」を取りあげて、まとめます。 animate*AsStateは、APIの中で最も汎用性があります。まず始めに抑えておくべきアニメーションAPIです。 ※環境:Android Studio Iguana | 2023.2.1     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.animation:* 1.5.0 ...
アニメーションの動きに特殊な効果を加えることができます。 特殊な効果とは、動きが加速したり、減速したり、弾んだり、または向きを変えたり、などです。 上記のような特殊な効果は、動きの軌道がそれぞれ異なる形になるため、ここでは「アニメーションの形状」と表現しています。 AnimationSpecはアニメーションの形状を定義するインターフェースです。そして、Jetpack Composeは実装済みの形状を4つ提供しています。 この中から、「spring」と取りあげて、まとめます。 ※環境:Android Studio Iguana | 2023.2.1 Patch 1     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.animation:* 1.5.0 ...
アニメーションの動きに特殊な効果を加えることができます。 特殊な効果とは、動きが加速したり、減速したり、弾んだり、または向きを変えたり、などです。 上記のような特殊な効果は、動きの軌道がそれぞれ異なる形になるため、ここでは「アニメーションの形状」と表現しています。 AnimationSpecはアニメーションの形状を定義するインターフェースです。そして、Jetpack Composeは実装済みの形状を4つ提供しています。 この中から、「tween」と取りあげて、まとめます。 ※環境:Android Studio Iguana | 2023.2.1 Patch 1     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.animation:* 1.5.0 ...
アニメーションの動きに特殊な効果を加えることができます。 特殊な効果とは、動きが加速したり、減速したり、弾んだり、または向きを変えたり、などです。 上記のような特殊な効果は、動きの軌道がそれぞれ異なる形になるため、ここでは「アニメーションの形状」と表現しています。 AnimationSpecはアニメーションの形状を定義するインターフェースです。そして、Jetpack Composeは実装済みの形状を4つ提供しています。 この中から、「keyframes」と取りあげて、まとめます。 ※環境:Android Studio Iguana | 2023.2.1 Patch 1     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.animation:* 1.5.0 ...
アニメーションの動きに特殊な効果を加えることができます。 特殊な効果とは、動きが加速したり、減速したり、弾んだり、または向きを変えたり、などです。 上記のような特殊な効果は、動きの軌道がそれぞれ異なる形になるため、ここでは「アニメーションの形状」と表現しています。 AnimationSpecはアニメーションの形状を定義するインターフェースです。そして、Jetpack Composeは実装済みの形状を4つ提供しています。 この中から、「snap」と取りあげて、まとめます。 ※環境:Android Studio Iguana | 2023.2.1 Patch 1     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.animation:* 1.5.0 ...
スポンサーリンク