Change-Id: Iaf4f80c34914022d79c7e089799fc6dd5554a532pull/2/head
parent
7ac15771b3
commit
2145f6a7cd
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2022 The Android Open Source Project
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package com.google.samples.apps.nowinandroid.core.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.animateColor
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.StartOffset
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.keyframes
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.rotate
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.samples.apps.nowinandroid.core.ui.theme.NiaTheme
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun LoadingWheel(
|
||||
contentDesc: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val infiniteTransition = rememberInfiniteTransition()
|
||||
|
||||
// Specifies the float animation for slowly drawing out the lines on entering
|
||||
val floatAnimValues = (0 until NUM_OF_LINES).map { remember { Animatable(1F) } }
|
||||
LaunchedEffect(floatAnimValues) {
|
||||
(0 until NUM_OF_LINES).map { index ->
|
||||
launch {
|
||||
floatAnimValues[index].animateTo(
|
||||
targetValue = 0F,
|
||||
animationSpec = tween(
|
||||
durationMillis = 100,
|
||||
easing = FastOutSlowInEasing,
|
||||
delayMillis = 40 * index
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Specifies the rotation animation of the entire Canvas composable
|
||||
val rotationAnim by infiniteTransition.animateFloat(
|
||||
initialValue = 0F,
|
||||
targetValue = 360F,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(durationMillis = ROTATION_TIME, easing = LinearEasing)
|
||||
)
|
||||
)
|
||||
|
||||
// Specifies the color animation for the base-to-progress line color change
|
||||
val baseLineColor = MaterialTheme.colorScheme.onBackground
|
||||
val progressLineColor = MaterialTheme.colorScheme.inversePrimary
|
||||
val colorAnimValues = (0 until NUM_OF_LINES).map { index ->
|
||||
infiniteTransition.animateColor(
|
||||
initialValue = baseLineColor,
|
||||
targetValue = baseLineColor,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = keyframes {
|
||||
durationMillis = ROTATION_TIME / 2
|
||||
progressLineColor at ROTATION_TIME / NUM_OF_LINES / 2 with LinearEasing
|
||||
baseLineColor at ROTATION_TIME / NUM_OF_LINES with LinearEasing
|
||||
},
|
||||
repeatMode = RepeatMode.Restart,
|
||||
initialStartOffset = StartOffset(ROTATION_TIME / NUM_OF_LINES / 2 * index)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Draws out the LoadingWheel Canvas composable and sets the animations
|
||||
Canvas(
|
||||
modifier = modifier
|
||||
.size(48.dp)
|
||||
.padding(8.dp)
|
||||
.graphicsLayer { rotationZ = rotationAnim }
|
||||
.semantics { contentDescription = contentDesc }
|
||||
) {
|
||||
repeat(NUM_OF_LINES) { index ->
|
||||
rotate(degrees = index * 30f) {
|
||||
drawLine(
|
||||
color = colorAnimValues[index].value,
|
||||
// Animates the initially drawn 1 pixel alpha from 0 to 1
|
||||
alpha = if (floatAnimValues[index].value < 1f) 1f else 0f,
|
||||
strokeWidth = 4F,
|
||||
cap = StrokeCap.Round,
|
||||
start = Offset(size.width / 2, size.height / 4),
|
||||
end = Offset(size.width / 2, floatAnimValues[index].value * size.height / 4)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(
|
||||
name = "Loading Wheel Light Preview",
|
||||
uiMode = Configuration.UI_MODE_NIGHT_NO,
|
||||
)
|
||||
@Preview(
|
||||
name = "Loading Wheel Dark Preview",
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
)
|
||||
@Composable
|
||||
fun LoadingWheelPreview() {
|
||||
NiaTheme {
|
||||
Surface {
|
||||
LoadingWheel(contentDesc = "LoadingWheel")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val ROTATION_TIME = 12000
|
||||
private const val NUM_OF_LINES = 12
|
Loading…
Reference in new issue