Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit 28c43c4

Browse files
authored
Merge pull request #95 from android/riggaroo/bouncy-loader
Added example of Animatable use case for custom Canvas animation
2 parents 47b67b8 + 75a9f02 commit 28c43c4

File tree

3 files changed

+117
-1
lines changed

3 files changed

+117
-1
lines changed

MotionCompose/app/src/main/java/com/example/android/compose/motion/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ class MainActivity : ComponentActivity() {
3131
Main()
3232
}
3333
}
34-
}
34+
}

MotionCompose/app/src/main/java/com/example/android/compose/motion/demo/Demo.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.example.android.compose.motion.demo
1818

1919
import androidx.compose.runtime.Composable
20+
import com.example.android.compose.motion.demo.custom.CustomCanvasBouncyLoader
2021
import com.example.android.compose.motion.demo.fade.FadeDemo
2122
import com.example.android.compose.motion.demo.fadethrough.FadeThroughDemo
2223
import com.example.android.compose.motion.demo.loading.LoadingDemo
@@ -101,5 +102,16 @@ enum class Demo(
101102
"Modifier.placeholder", "AnimationSpec"
102103
),
103104
content = { LoadingDemo() }
105+
),
106+
CustomCanvasAnimation(
107+
title = "Custom > Custom Drawing Canvas Animation",
108+
description = """
109+
A custom loading animation example using Canvas and draw APIs, combined with
110+
Animatable to show the use of the animateTo() function used sequentially.
111+
""".trimIndent().replace('\n', ' '),
112+
apis = listOf(
113+
"Animatable"
114+
),
115+
content = { CustomCanvasBouncyLoader() }
104116
)
105117
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2022 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.example.android.compose.motion.demo.custom
17+
18+
import androidx.compose.animation.core.Animatable
19+
import androidx.compose.animation.core.Spring
20+
import androidx.compose.animation.core.SpringSpec
21+
import androidx.compose.animation.core.spring
22+
import androidx.compose.foundation.Canvas
23+
import androidx.compose.foundation.layout.Box
24+
import androidx.compose.foundation.layout.fillMaxSize
25+
import androidx.compose.foundation.layout.size
26+
import androidx.compose.runtime.Composable
27+
import androidx.compose.runtime.LaunchedEffect
28+
import androidx.compose.runtime.mutableStateOf
29+
import androidx.compose.runtime.remember
30+
import androidx.compose.ui.Alignment
31+
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.graphics.Brush
33+
import androidx.compose.ui.graphics.Color
34+
import androidx.compose.ui.graphics.graphicsLayer
35+
import androidx.compose.ui.layout.onSizeChanged
36+
import androidx.compose.ui.tooling.preview.Preview
37+
import androidx.compose.ui.unit.IntSize
38+
import androidx.compose.ui.unit.dp
39+
import kotlinx.coroutines.delay
40+
41+
@Composable
42+
@Preview
43+
fun CustomCanvasBouncyLoader() {
44+
val yOffset = remember {
45+
Animatable(0f)
46+
}
47+
val scale = remember {
48+
Animatable(1f)
49+
}
50+
LaunchedEffect("bouncyLoader") {
51+
delay(400)
52+
// We use the Animatable.animateTo() API here to demonstrate the coroutine usage - each
53+
// item is animating one after the other, as the animateTo function is sequential.
54+
// Animate y to half the height
55+
yOffset.animateTo(0.5f, bouncyAnimationSpec)
56+
scale.animateTo(3f, bouncyAnimationSpec)
57+
delay(500)
58+
scale.animateTo(10f, bouncyAnimationSpec)
59+
delay(500)
60+
scale.animateTo(50f, bouncyAnimationSpec)
61+
}
62+
val size = remember {
63+
mutableStateOf(IntSize.Zero)
64+
}
65+
Box(
66+
Modifier
67+
.fillMaxSize()
68+
.onSizeChanged {
69+
// We get the size change of the whole composable, and use this to determine how
70+
// big the ball should be.
71+
size.value = it
72+
}) {
73+
GradientCircle(
74+
Modifier
75+
.align(Alignment.TopCenter)
76+
.size(26.dp)
77+
.graphicsLayer {
78+
// We use .graphicsLayer here to perform the animation as we are animating
79+
// multiple properties of our Gradient circle at once, and this is more
80+
// efficient than using multiple modifiers.
81+
// .graphicsLayer also defers these changes to the Draw phase of Compose,
82+
// therefore minimizing recompositions required to do this.
83+
scaleX = scale.value
84+
scaleY = scale.value
85+
translationY = yOffset.value * size.value.height
86+
}
87+
)
88+
}
89+
}
90+
91+
@Composable
92+
private fun GradientCircle(modifier: Modifier = Modifier) {
93+
val brush = remember {
94+
Brush.verticalGradient(listOf(Color(0xFFF56E34), Color(0xFF234EDA)))
95+
}
96+
Canvas(modifier = modifier) {
97+
drawCircle(brush = brush)
98+
}
99+
}
100+
101+
private val bouncyAnimationSpec: SpringSpec<Float> = spring(
102+
dampingRatio = Spring.DampingRatioMediumBouncy,
103+
stiffness = Spring.StiffnessLow
104+
)

0 commit comments

Comments
 (0)