Jetpack Compose實現動畫效果的方法詳解
概述
compose 為支持動畫提供瞭大量的 api,通過這些 api 我們可以輕松實現動畫效果
ps:這些 api 的原理與 Flutter 很接近,與原生的 api 相去甚遠
你可以提前看看用 compose 實現的一個放大縮小動畫,總的來說還是比較流暢:
低級別動畫 API
animate*AsState
所能處理屬性的種類:Float、Color、Dp、Size、Bounds、Offset、Rect、Int、IntOffset 和 IntSize
通過 animate*AsState 我們可以實現單一屬性的動畫效果,我們隻需要提供目標值就可以自動從當前進度動畫過渡到目標值
實現放大動畫
1.代碼
@Composable fun animSize() { val enable = remember { mutableStateOf(true) } val size = animateSizeAsState(targetValue = if (enable.value) Size(50f, 50f) else Size(300f, 300f)) Column( modifier = Modifier.fillMaxSize(1f), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Image( modifier = Modifier .size(size.value.width.dp, size.value.height.dp) .clickable { enable.value = !enable.value }, painter = painterResource(id = R.drawable.apple), contentDescription = "" ) } }
2.實現效果
實現顏色變化動畫
1.代碼
@Composable fun animColor() { val enable = remember { mutableStateOf(true) } val colors = animateColorAsState(targetValue = if (enable.value) Color.Green else Color.Red) val size = animateIntSizeAsState( targetValue = if (enable.value) IntSize(100, 100) else IntSize( 300, 300 ) ) Column( modifier = Modifier.fillMaxWidth(1f), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Box( modifier = Modifier .size(size.value.width.dp, size.value.height.dp) .height(400.dp) .background( color = colors.value, shape = if (enable.value) RectangleShape else CircleShape ) ) { } } }
2.效果
使用 Animatable 實現顏色變化效果
Animatable 是一個值容器,我們可以通過調用 animateTo 實現動畫效果。動畫執行過程中如果再次開啟動畫會中斷當前動畫。
Animatable 動畫執行過程中值的變化是在協程中執行的,所以 animateTo 是一個掛起操作
1.代碼
@Composable fun animChangeColor() { val color = remember { Animatable(Color.Red) } val state = remember { mutableStateOf(true) } LaunchedEffect(state.value) { color.animateTo(if (state.value) Color.Red else Color.Magenta) } Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.Center) { Box( modifier = Modifier .background(color.value, shape = RoundedCornerShape(30.dp)) .size(200.dp) .clickable { state.value = !state.value }, contentAlignment = Alignment.Center ) { Text( text = "顏色動畫", style = TextStyle(color = Color.White, fontSize = 40.sp) ) } } }
2.效果
使用 updateTransition 實現顏色和圓角動畫
使用 updateTransition 可以實現多個動畫組合的效果。
例如:我們可以在動畫執行過程中同時執行大小和顏色變化效果
本例中我們定義瞭一個枚舉用來控制動畫,枚舉可以定義多個,分別用來對應動畫的多個狀態
1.代碼
@Composable fun animupdateTransition() { var state by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(targetState = state, label = "") val round = transition.animateDp(label = "") { when (it) { BoxState.Collapsed -> 40.dp BoxState.Expanded -> 100.dp } } val color = transition.animateColor(label = "") { when (it) { BoxState.Collapsed -> Color.Red BoxState.Expanded -> Color.Green } } Box(Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) { Box( modifier = Modifier .size(300.dp) .background( color.value, shape = RoundedCornerShape(corner = CornerSize(round.value)) ) .clickable { state = if (state == BoxState.Collapsed) BoxState.Expanded else BoxState.Collapsed },contentAlignment = Alignment.Center ) { Text(text = "點擊開始動畫",style = TextStyle(color = Color.White,fontSize = 20.sp)) } } } private enum class BoxState { Collapsed, Expanded }
2.效果
rememberInfiniteTransition
rememberInfiniteTransition 的使用和 updateTransition 基本一樣,不同的是 rememberInfiniteTransition 的動畫一旦開始便會一直反復運行下去,隻有被移除動畫才能結束
1.代碼
@Composable fun rememberInfiniteTransition1() { val infiniteTransition = rememberInfiniteTransition() val color by infiniteTransition.animateColor( initialValue = Color.Red, targetValue = Color.Green, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ) ) Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.Center) { Box( Modifier .fillMaxSize(0.8f) .background(color), contentAlignment = Alignment.Center ) { Text( text = "公眾號:安安安安卓 原創,禁抄襲", style = TextStyle(color = Color.White, fontSize = 30.sp) ) } } }
2.效果
TargetBasedAnimation
TargetBasedAnimation 可以控制動畫的執行時間,還可以延遲一段時間再開啟動畫。
1.代碼
@Composable fun animTargetBasedAnimation() { var state by remember { mutableStateOf(0) } val anim = remember { TargetBasedAnimation( animationSpec = tween(2000), typeConverter = Float.VectorConverter, initialValue = 100f, targetValue = 300f ) } var playTime by remember { mutableStateOf(0L) } var animationValue by remember { mutableStateOf(0) } LaunchedEffect(state) { val startTime = withFrameNanos { it } println("進入協程:") do { playTime = withFrameNanos { it } - startTime animationValue = anim.getValueFromNanos(playTime).toInt() } while (!anim.isFinishedFromNanos(playTime)) } Box(modifier = Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) { Box(modifier = Modifier .size(animationValue.dp) .background(Color.Red,shape = RoundedCornerShape(animationValue/5)) .clickable { state++ },contentAlignment = Alignment.Center) { Text(text = animationValue.toString(),style = TextStyle(color = Color.White,fontSize = (animationValue/5).sp)) } } }
2.效果
自定義動畫
AnimationSpec
AnimationSpec 可以自定義動畫的行為,效果類似於原生動畫中的估值器。
SpringSpec 彈簧效果
1.代碼
@Composable fun animSpring() { val state = remember { mutableStateOf(true) } var value = animateIntAsState( targetValue = if (state.value) 300 else 100, animationSpec = spring( dampingRatio = Spring.DampingRatioHighBouncy, stiffness = Spring.StiffnessVeryLow ) ) Box( Modifier .fillMaxSize(1f) .padding(start = 30.dp), contentAlignment = Alignment.CenterStart ) { Box( Modifier .width(value.value.dp) .height(80.dp) .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp)) .clickable { state.value = !state.value }, contentAlignment = Alignment.CenterStart ) { Text(text = "哈哈哈", style = TextStyle(color = Color.White, fontSize = 20.sp)) } } }
2.效果
TweenSpec 動畫時間可控
1.代碼
@Composable fun animTweenSpec() { val state = remember { mutableStateOf(true) } val value = animateIntAsState( targetValue = if (state.value) 300 else 100, animationSpec = tween( durationMillis = 1500, delayMillis = 200, easing = LinearEasing ) ) Box( Modifier .fillMaxSize(1f) .padding(start = 50.dp), contentAlignment = Alignment.CenterStart ) { Box( Modifier .width(value.value.dp) .height(100.dp) .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp)) .clickable { state.value = !state.value } ) { } } }
2.效果
FrameSpec
1.代碼
@Composable fun animkeyframesSpec() { var state by remember { mutableStateOf(true) } val value by animateIntAsState( targetValue = if (state) 300 else 100, animationSpec = keyframes { durationMillis = 2000 0 at 700 with LinearOutSlowInEasing 700 at 1400 with FastOutLinearInEasing 1400 at 2000 }) Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.CenterStart) { Box( Modifier .width(value.dp) .height(100.dp) .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp)) .clickable { state = !state } ) { } } }
2.效果
RepeatableSpec 實現有限次數的重復動畫
執行有限次數動畫後自動停止
1.代碼
@Composable fun animrepeatableSpec() { var state by remember { mutableStateOf(true) } val value by animateIntAsState( targetValue = if (state) 300 else 100, animationSpec = repeatable( iterations = 5,//動畫重復執行的次數,設置多少就執行多少次 animation = tween(durationMillis = 1000), repeatMode = RepeatMode.Reverse ) ) Box( Modifier .fillMaxSize(1f) .padding(start = 30.dp), contentAlignment = Alignment.CenterStart) { Box( Modifier .width(value.dp) .height(100.dp) .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp)) .clickable { state = !state } ) { } } }
2.效果
代碼中設置瞭重復 5 次,所以反復執行五次後動畫結束
InfiniteRepeatableSpec 無限次數執行動畫
動畫會無限次的執行下去,直到視圖被移除
1.代碼
@Composable fun animinfiniteRepeatableSpec() { var state by remember { mutableStateOf(true) } val value by animateIntAsState( targetValue = if (state) 300 else 100, animationSpec = infiniteRepeatable( animation = tween(durationMillis = 1000), repeatMode = RepeatMode.Reverse ) ) Box( Modifier .fillMaxSize(1f) .padding(start = 30.dp), contentAlignment = Alignment.CenterStart) { Box( Modifier .width(value.dp) .height(100.dp) .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp)) .clickable { state = !state } ) { Text(text = "公眾號:安安安安卓 原創,禁轉載") } } }
2.效果
Easing
Easing 類似於我們原生動畫中的差值器
有以下幾種選擇:
- FastOutSlowInEasing
- LinearOutSlowInEasing
- FastOutLinearInEasing
- LinearEasing
- CubicBezierEasing
這幾種實現的效果和 android 原生實現的動畫差值器差距很大,甚至看不出有啥效果,所以代碼我就不放瞭。有清楚原因的讀者可以聯系我
實現效果:
AnimationVector
大多數 Compose 動畫 API 都支持將 Float、Color、Dp 以及其他基本數據類型作為開箱即用的動畫值,但有時我們需要為其他數據類型(包括我們的自定義類型)添加動畫效果
本例中實現顏色和大小的變換動畫
代碼中我們定義瞭一個 AnimSize 類,類中的第一個參數是顏色數據,第二個參數是尺寸數據。動畫執行過程中會同事改變顏色和控件尺寸效果。
1.代碼
@Composable fun animAnimationVector() { var state by remember { mutableStateOf(true) } val value by animateValueAsState( targetValue = if (state) AnimSize(0xffff5500, 100f) else AnimSize(0xff00ff00, 300f), typeConverter = TwoWayConverter( convertToVector = { // AnimationVector2D(target.color.toFloat(), target.size) AnimationVector2D(it.color.toFloat(), it.size) }, convertFromVector = { AnimSize(it.v1.toLong(), it.v2) } ) ) println("顏色:${value.color}") Box(modifier = Modifier.fillMaxSize(1f).padding(30.dp), contentAlignment = Alignment.Center) { Box( modifier = Modifier .size(value.size.dp) // .size(300.dp) .background(Color(value.color), RoundedCornerShape(30.dp)) .clickable { state = !state } ) { } } } data class AnimSize(val color: Long, val size: Float)
2.效果
缺點是執行顏色變化過程中有閃爍
高級動畫
高級動畫一般指封裝性較高的動畫,使用較為簡單,主要有以下三種:
因高級動畫效果不明顯,gif 很難展現出效果,所以這裡不放代碼和效果圖瞭
- AnimatedVisibility
- animateContentSize
- Crossfade
以上就是Jetpack Compose實現動畫效果的方法詳解的詳細內容,更多關於Jetpack Compose動畫的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Jetpack Compose實現列表和動畫效果詳解
- 利用Jetpack Compose繪制可愛的天氣動畫
- Android Compose實現底部按鈕以及首頁內容詳細過程第1/2頁
- Android開發Compose remember原理解析
- Jetpack Compose佈局的使用詳細介紹