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