Choreograph your Compose Animation ππΊ
A lightweight Compose Animation utility library to choreograph low-level Animation API (https://developer.android.com/jetpack/compose/animation#animation) through Kotlin DSL. It does the heavy lifting of dealing with coroutines under the hood so that you can focus on your animation choreography.
Koreography is available on mavenCentral()
implementation 'io.github.sagar-viradiya:koreography:0.3.0'
Creating choreography is the process of recording moves on Animatable, A low level compose animation API. Moves could be either parallel or sequential. A choreography is a composition of such moves that you can declare through clean and concise Kotlin DSL as shown below.
For example, let's say you want to sequentially animate alpha first and then scale of an image then you can declare the following choreography. You would then apply animatable values that you are animating in choreography in the Image composable as shown below.
val alphaAnimatable = remember {
Animatable(0f)
}
val scaleAnimatable = remember {
Animatable(0f)
}
val koreography = rememberKoreography {
move(
animatable = alphaAnimatable,
targetValue = 1f,
animationSpec = tween(500)
)
move(
animatable = scaleAnimatable,
targetValue = 2f,
animationSpec = tween(500)
)
}
.
.
.
Image(
modifier = Modifier.graphicsLayer {
alpha = alphaAnimatable.value
scaleX = scaleAnimatable.value
},
painter = painterResource(id = R.drawable.image)
)
Let's say you want to parallelly animate alpha and scale of an image then you can declare the following choreography.
val alphaAnimatable = remember {
Animatable(0f)
}
val scaleAnimatable = remember {
Animatable(0f)
}
val koreography = rememberKoreography {
parallelMoves {
move(
animatable = alphaAnimatable,
targetValue = 1f,
animationSpec = tween(500)
)
move(
animatable = scaleAnimatable,
targetValue = 2f,
animationSpec = tween(500)
)
}
}
.
.
.
Image(
modifier = Modifier.graphicsLayer {
alpha = alphaAnimatable.value
scaleX = scaleAnimatable.value
},
painter = painterResource(id = R.drawable.image)
)
You can have a nested hierarchy of moves to create complex choreography. The example below has three animations running parallelly and out of them, the last one has two animations within running sequentially.
val alphaAnimatable = remember {
Animatable(0f)
}
val scaleAnimatable = remember {
Animatable(0f)
}
val rotationAnimatable = remember {
Animatable(0f)
}
val translationXAnimatable = remember {
Animatble(0f)
}
val koreography = rememberKoreography {
parallelMoves {
move(
animatable = alphaAnimatable,
targetValue = 1f,
animationSpec = tween(500)
)
move(
animatable = scaleAnimatable,
targetValue = 2f,
animationSpec = tween(500)
)
sequentialMoves {
move(
animatable = rotationAnimatable,
targetValue = 20f,
animationSpec = tween(500)
)
move(
animatable = translationXAnimatable,
targetValue = 200f,
animationSpec = tween(500)
)
}
}
}
.
.
.
Image(
modifier = Modifier.graphicsLayer {
alpha = alphaAnimatable.value
scaleX = scaleAnimatable.value
translationX = translationXAnimatable.value
rotationZ = rotationAnimatable.value
},
painter = painterResource(id = R.drawable.image)
)
Once choreography is ready it's time to dance! ππΊ.
You can execute the choreography once by calling dance
function. If you wish to get callback after execution of choreography then you can pass trailing lambda.
koreography.dance(scope = coroutineScope) {
// onDanceFinished : Optional trailing lambda
}
You can execute the choreography multiple times by calling repeatDance
function. Following call executes choreography three times.
koreography.repeatDance(count = 3, scope = coroutineScope) {
// onDanceFinished : Optional trailing lambda
}
You can execute the choreography forever until composition is alive.
koreography.danceForever(scope = coroutineScope){
// onDanceFinished : Optional trailing lambda
}
Please note the
coroutineScope
should be obtained throughrememberCoroutineScope()
. Make sure you pass coroutine scope which will get cleared once you exit composition.
Executing choreography based on state change is also supported. This API is similar to LaunchedEffect
API of compose side effects.
In the following example, we are executing choreography passed in the trailing lambda of LaunchKoreography
composable on a state value change. Choreography contains two animations that will be executed sequentially.
val alphaAnimatable = remember {
Animatable(0f)
}
val scaleAnimatable = remember {
Animatable(0f)
}
LaunchKoreography(state) {
move(
animatbale = alphaAnimatable,
targetValue = 1f,
animationSpec = tween(500)
)
move(
animatbale = scaleAnimatable,
targetValue = 2f,
animationSpec = tween(500)
)
}
.
.
.
Image(
modifier = Modifier.graphicsLayer {
alpha = alphaAnimatable.value
scaleX = scaleAnimatable.value
},
painter = painterResource(id = R.drawable.image)
)
There is endless possibilities with power of coroutines and compose animation API! Here are some free lottie animations recreated using koreography. You can find the source code in the sample app.
screen-20230611-113322.2.mov
screen-20230611-113358.3.mp4
screen-20230611-113420.2.mp4
screen-20230611-113444.2.mov
meditaion_animation.mov
This is the early preview and unfortunately it is not ready to accept any contribution yet. Once this is stable enough contribution guidelines will be updated here. Meanwhile feel free to start GitHub Discussions for feature request and improvements.
Copyright 2022 Koreography Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.