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